1181 lines
49 KiB
JavaScript
1181 lines
49 KiB
JavaScript
// Bing Wallpaper GNOME extension
|
||
// Copyright (C) 2017-2025 Michael Carroll
|
||
// This extension is free software: you can redistribute it and/or modify
|
||
// it under the terms of the GNU Lesser General Public License as published by
|
||
// the Free Software Foundation, either version 3 of the License, or
|
||
// (at your option) any later version.
|
||
// See the GNU General Public License, version 3 or later for details.
|
||
// Based on GNOME shell extension NASA APOD by Elia Argentieri https://github.com/Elinvention/gnome-shell-extension-nasa-apod
|
||
|
||
import St from 'gi://St';
|
||
import Soup from 'gi://Soup';
|
||
import Gio from 'gi://Gio';
|
||
import GObject from 'gi://GObject';
|
||
import GLib from 'gi://GLib';
|
||
import Clutter from 'gi://Clutter';
|
||
import Cogl from 'gi://Cogl';
|
||
|
||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||
import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';
|
||
import {Button} from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||
import * as Config from 'resource:///org/gnome/shell/misc/config.js';
|
||
|
||
import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||
import * as Utils from './utils.js';
|
||
import Blur from './blur.js';
|
||
import Thumbnail from './thumbnail.js';
|
||
import BWClipboard from './BWClipboard.js';
|
||
|
||
const BingImageURL = Utils.BingImageURL;
|
||
const BingURL = 'https://www.bing.com';
|
||
const IndicatorName = 'BingWallpaperIndicator';
|
||
const TIMEOUT_SECONDS = 24 * 3600; // FIXME: this should use the end data from the json data
|
||
const TIMEOUT_SECONDS_ON_HTTP_ERROR = 1 * 3600; // retry in one hour if there is a http error3
|
||
const MINIMUM_SHUFFLE_IMAGES = 3; // bare minimum to use filtered image set in shuffle mode
|
||
const ICON_PREVIOUS_BUTTON = 'media-seek-backward-symbolic';
|
||
const ICON_NEXT_BUTTON = 'media-seek-forward-symbolic';
|
||
const ICON_CURRENT_BUTTON = 'media-skip-forward-symbolic';
|
||
|
||
let bingWallpaperIndicator = null;
|
||
let blur = null;
|
||
|
||
const newMenuItem = (label) => {
|
||
return new PopupMenu.PopupMenuItem(label);
|
||
}
|
||
|
||
const newMenuSwitchItem = (label, state) => {
|
||
let switchItem = new PopupMenu.PopupSwitchMenuItem(
|
||
label,
|
||
state,
|
||
{});
|
||
switchItem.label.x_expand = true;
|
||
switchItem._statusBin.x_expand = false;
|
||
return switchItem;
|
||
}
|
||
|
||
function BingLog(msg) {
|
||
if (BingDebug())
|
||
console.log('BingWallpaper extension: ' + msg); // disable to keep the noise down in journal
|
||
}
|
||
|
||
function BingDebug() {
|
||
if (bingWallpaperIndicator && bingWallpaperIndicator._settings.get_boolean('debug-logging'))
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
function notifyError(msg) {
|
||
Main.notifyError("BingWallpaper extension error", msg);
|
||
}
|
||
|
||
function doSetBackground(uri, schema) {
|
||
let gsettings = new Gio.Settings({schema: schema});
|
||
let prev = gsettings.get_string('picture-uri');
|
||
uri = 'file://' + uri;
|
||
gsettings.set_string('picture-uri', uri);
|
||
try {
|
||
gsettings.set_string('picture-uri-dark', uri);
|
||
}
|
||
catch (e) {
|
||
BingLog("unable to set dark background for : " + e);
|
||
}
|
||
Gio.Settings.sync();
|
||
gsettings.apply();
|
||
return (prev != uri); // return true if background uri has changed
|
||
}
|
||
|
||
const BingWallpaperIndicator = GObject.registerClass(
|
||
class BingWallpaperIndicator extends Button {
|
||
_init(ext) {
|
||
super._init(0, IndicatorName, false);
|
||
|
||
this.title = "";
|
||
this.explanation = "";
|
||
this.filename = "";
|
||
this.copyright = "";
|
||
this.version = "0.1";
|
||
this._updatePending = false;
|
||
this._timeout = null;
|
||
this._shuffleTimeout = null;
|
||
this.longstartdate = null;
|
||
this.imageURL = ""; // link to image itself
|
||
this.imageinfolink = ""; // link to Bing photo info page
|
||
this.refreshdue = 0;
|
||
this.shuffledue = 0;
|
||
this.refreshduetext = "";
|
||
this.thumbnail = null;
|
||
this.thumbnailItem = null;
|
||
this.selected_image = "current";
|
||
this.clipboard = new BWClipboard();
|
||
this.imageIndex = null;
|
||
this.logger = null;
|
||
this.favourite_status = false;
|
||
this.hidden_status = false;
|
||
this.dimensions = { 'width': null, 'height': null};
|
||
this._extension = ext;
|
||
|
||
let extensionIconsPath = ext.dir.get_child('icons').get_path();
|
||
this.ICON_RANDOM = extensionIconsPath + '/'+'game-die-symbolic.svg';
|
||
this.ICON_FAVE_BUTTON = extensionIconsPath + '/'+'fav-symbolic.svg';
|
||
this.ICON_UNFAVE_BUTTON = extensionIconsPath + '/'+'unfav-symbolic.svg';
|
||
this.ICON_TRASH_BUTTON = extensionIconsPath + '/'+'trash-empty-symbolic.svg';
|
||
this.ICON_UNTRASH_BUTTON = extensionIconsPath + '/'+'trash-full-symbolic.svg';
|
||
|
||
if (!blur) // as Blur isn't disabled on screen lock (like the rest of the extension is)
|
||
blur = new Blur();
|
||
|
||
// take a variety of actions when the gsettings values are modified by prefs
|
||
this._settings = this._extension.getSettings();
|
||
|
||
// create Soup session
|
||
this._initSoup();
|
||
|
||
this.visible = !this._settings.get_boolean('hide');
|
||
|
||
this.refreshDueItem = newMenuItem(_("<No refresh scheduled>"));
|
||
this.explainItem = newMenuItem(_("Awaiting refresh..."));
|
||
this.copyrightItem = newMenuItem(_("Awaiting refresh..."));
|
||
this.clipboardImageItem = newMenuItem(_("Copy image to clipboard"));
|
||
this.clipboardURLItem = newMenuItem(_("Copy image URL to clipboard"));
|
||
this.folderItem = newMenuItem(_("Open image folder"));
|
||
this.dwallpaperItem = newMenuItem(_("Set background image"));
|
||
this.swallpaperItem = newMenuItem(_("Set lock screen image"));
|
||
this.refreshItem = newMenuItem(_("Refresh Now"));
|
||
this.settingsItem = newMenuItem(_("Settings"));
|
||
this.openImageItem = newMenuItem(_("Open in image viewer"));
|
||
this.openImageInfoLinkItem = newMenuItem(_("Open Bing image information page"));
|
||
this.imageResolutionItem = newMenuItem(_("Awaiting refresh..."));
|
||
|
||
this.titleItem = new PopupMenu.PopupSubMenuMenuItem(_("Awaiting refresh..."), false);
|
||
[this.imageResolutionItem, this.openImageInfoLinkItem, this.openImageItem, this.folderItem,
|
||
this.clipboardImageItem, this.clipboardURLItem, this.dwallpaperItem]
|
||
.forEach(e => this.titleItem.menu.addMenuItem(e));
|
||
|
||
// quick settings submenu
|
||
this.settingsSubMenu = new PopupMenu.PopupSubMenuMenuItem(_("Quick settings"), false);
|
||
// toggles under the quick settings submenu
|
||
this.toggleSetBackground = newMenuSwitchItem(_("Set background image"), this._settings.get_boolean('set-background'));
|
||
this.toggleSelectNew = newMenuSwitchItem(_("Always show new images"), this._settings.get_boolean('revert-to-current-image'));
|
||
this.toggleShuffle = newMenuSwitchItem(_("Image shuffle mode"), true);
|
||
this.toggleShuffleOnlyFaves = newMenuSwitchItem(_("Image shuffle only favorites"), this._settings.get_boolean('random-mode-include-only-favourites'));
|
||
/*this.toggleNotifications = newMenuSwitchItem(_("Enable desktop notifications"), this._settings.get_boolean('notify'));*/
|
||
this.toggleImageCount = newMenuSwitchItem(_("Show image count"), this._settings.get_boolean('show-count-in-image-title'));
|
||
this.toggleShuffleOnlyUHD = newMenuSwitchItem(_("Image shuffle only UHD resolutions"), this._settings.get_boolean('random-mode-include-only-uhd'));
|
||
|
||
[/*this.toggleNotifications, this.toggleImageCount, this.toggleSetBackground,*/ this.toggleSelectNew,
|
||
this.toggleShuffle, this.toggleShuffleOnlyFaves, this.toggleShuffleOnlyUHD]
|
||
.forEach(e => this.settingsSubMenu.menu.addMenuItem(e));
|
||
|
||
// these items are a bit unique, we'll populate them in _setControls()
|
||
this.controlItem = newMenuItem("");
|
||
this.thumbnailItem = new PopupMenu.PopupBaseMenuItem({ style_class: 'wp-thumbnail-image'});
|
||
this._setControls(); // build the button bar
|
||
|
||
// we need to word-wrap these menu items to not overflow menu in case of long lines of text
|
||
[this.refreshDueItem, this.titleItem, this.explainItem, this.copyrightItem]
|
||
.forEach((e, i) => {
|
||
this._wrapLabelItem(e);
|
||
});
|
||
|
||
// set the order of menu items (including separators)
|
||
let allMenuItems = [
|
||
this.refreshItem,
|
||
this.refreshDueItem,
|
||
new PopupMenu.PopupSeparatorMenuItem(),
|
||
this.controlItem,
|
||
new PopupMenu.PopupSeparatorMenuItem(),
|
||
this.explainItem,
|
||
this.thumbnailItem,
|
||
this.titleItem,
|
||
this.copyrightItem,
|
||
new PopupMenu.PopupSeparatorMenuItem(),
|
||
this.settingsSubMenu,
|
||
this.settingsItem
|
||
];
|
||
allMenuItems.forEach(e => this.menu.addMenuItem(e));
|
||
|
||
// non clickable information items
|
||
[this.explainItem, this.copyrightItem, this.refreshDueItem, this.thumbnailItem, this.imageResolutionItem]
|
||
.forEach((e) => {
|
||
e.setSensitive(false);
|
||
});
|
||
|
||
if (this._settings.get_boolean('random-mode-enabled')) {
|
||
[this.toggleShuffleOnlyFaves, this.toggleShuffleOnlyUHD]
|
||
.forEach((e) => {
|
||
e.setSensitive(false);
|
||
});
|
||
}
|
||
|
||
this._setConnections();
|
||
|
||
if (this._settings.get_string('state') != '[]') { // setting state on reset or initial boot
|
||
this._reStoreState();
|
||
}
|
||
else {
|
||
this._restartTimeout(60); // wait 60 seconds before performing refresh
|
||
}
|
||
}
|
||
|
||
// create Soup session
|
||
_initSoup() {
|
||
this.httpSession = new Soup.Session();
|
||
this.httpSession.user_agent = 'User-Agent: Mozilla/5.0 (X11; GNOME Shell/' + Config.PACKAGE_VERSION + '; Linux x86_64; +https://github.com/neffo/bing-wallpaper-gnome-extension ) BingWallpaper Gnome Extension/' + this._extension.metadata.version;
|
||
}
|
||
|
||
// listen for configuration changes
|
||
_setConnections() {
|
||
this.settings_connections = [];
|
||
|
||
this.settings_connections.push(
|
||
this._settings.connect('changed::hide', () => {
|
||
this.visible = !this._settings.get_boolean('hide');
|
||
})
|
||
);
|
||
|
||
let settingConnections = [
|
||
{signal: 'changed::icon-name', call: this._setIcon},
|
||
{signal: 'changed::market', call: this._refresh},
|
||
{signal: 'changed::set-background', call: this._setBackground},
|
||
{signal: 'changed::override-lockscreen-blur', call: this._setBlur},
|
||
{signal: 'changed::lockscreen-blur-strength', call: this._setBlur},
|
||
{signal: 'changed::lockscreen-blur-brightness', call: this._setBlur},
|
||
{signal: 'changed::selected-image', call: this._setImage},
|
||
{signal: 'changed::delete-previous', call: this._cleanUpImages},
|
||
{signal: 'changed::notify', call: this._notifyCurrentImage},
|
||
{signal: 'changed::always-export-bing-json', call: this._exportData},
|
||
{signal: 'changed::bing-json', call: this._exportData},
|
||
{signal: 'changed::controls-icon-size', call: this._setControls},
|
||
{signal: 'changed::random-mode-enabled', call: this._randomModeChanged},
|
||
{signal: 'changed::random-mode-include-only-favourites', call: this._randomModeChanged},
|
||
{signal: 'changed::random-mode-include-only-unhidden', call: this._randomModeChanged},
|
||
{signal: 'changed::random-mode-include-only-uhd', call: this._randomModeChanged},
|
||
{signal: 'changed::random-interval-mode', call: this._randomModeChanged}
|
||
];
|
||
|
||
// _setShuffleToggleState
|
||
settingConnections.forEach((e) => {
|
||
this.settings_connections.push(
|
||
this._settings.connect(e.signal, e.call.bind(this))
|
||
);
|
||
});
|
||
|
||
// ensure we're in a sensible initial state
|
||
this._setIcon();
|
||
this._setBlur();
|
||
this._setImage();
|
||
this._cleanUpImages();
|
||
|
||
// menu connections
|
||
this.connect('button-press-event', this._openMenu.bind(this));
|
||
|
||
// link menu items to functions
|
||
//this.thumbnailItem.connect('activate', this._setBackgroundDesktop.bind(this));
|
||
this.thumbnailItem.connect('activate', this._openInSystemViewer.bind(this));
|
||
this.openImageItem.connect('activate', this._openInSystemViewer.bind(this));
|
||
//this.titleItem.connect('activate', this._setBackgroundDesktop.bind(this));
|
||
this.openImageInfoLinkItem.connect('activate', this._openImageInfoLink.bind(this));
|
||
this.dwallpaperItem.connect('activate', this._setBackgroundDesktop.bind(this));
|
||
this.refreshItem.connect('activate', this._refresh.bind(this));
|
||
this.settingsItem.connect('activate', this._openPrefs.bind(this));
|
||
|
||
// unfortunately we can't bind toggles trivially like we can with prefs.js here, so we handle toggles in two steps
|
||
// first, we listen for changes to these toggle settings and update the status
|
||
// & then, link settings to toggle state (the other way)
|
||
|
||
let toggles = [ /*{key: 'set-background', toggle: this.toggleSetBackground},*/
|
||
{key: 'revert-to-current-image', toggle: this.toggleSelectNew},
|
||
/*{key: 'notify', toggle: this.toggleNotifications},
|
||
{key: 'show-count-in-image-title', toggle: this.toggleImageCount},*/
|
||
{key: 'random-mode-enabled', toggle: this.toggleShuffle},
|
||
{key: 'random-mode-include-only-favourites', toggle: this.toggleShuffleOnlyFaves},
|
||
/*{key: 'random-mode-include-only-unhidden', toggle: this.toggleShuffleOnlyUnhidden},*/
|
||
{key: 'random-mode-include-only-uhd', toggle: this.toggleShuffleOnlyUHD}];
|
||
|
||
toggles.forEach( (e) => {
|
||
this.settings_connections.push(
|
||
this._settings.connect('changed::'+e.key, () => {
|
||
BingLog(e.key+' setting changed to '+ (this._settings.get_boolean(e.key)?'true':'false'));
|
||
e.toggle.setToggleState(this._settings.get_boolean(e.key));
|
||
})
|
||
);
|
||
e.toggle.connect('toggled', (item, state) => {
|
||
BingLog(e.key+' switch toggled to '+ (state?'true':'false'));
|
||
this._setBooleanSetting(e.key, state);
|
||
});
|
||
});
|
||
|
||
this.folderItem.connect('activate', Utils.openImageFolder.bind(this, this._settings));
|
||
if (this.clipboard.clipboard) { // only if we have a clipboard
|
||
this.clipboardImageItem.connect('activate', this._copyImageToClipboard.bind(this));
|
||
this.clipboardURLItem.connect('activate', this._copyURLToClipboard.bind(this));
|
||
}
|
||
else {
|
||
[this.clipboardImageItem, this.clipboardURLItem].
|
||
forEach(e => e.setSensitive(false));
|
||
}
|
||
}
|
||
|
||
_setBooleanSetting(key, state) {
|
||
let success = this._settings.set_boolean(key, state);
|
||
BingLog('key '+key+' set to ' + (state?'true':'false') + ' (returned ' + (success?'true':'false')+')');
|
||
}
|
||
|
||
_setStringSetting(key, value) {
|
||
let success = this._settings.set_string(key, value);
|
||
BingLog('key '+key+' set to ' + value + ' (returned ' + (success?'true':'false')+')');
|
||
}
|
||
|
||
_setIntSetting(key, value) {
|
||
let success = this._settings.set_int(key, value);
|
||
BingLog('key '+key+' set to ' + value + ' (returned ' + (success?'true':'false')+')');
|
||
}
|
||
|
||
_onDestroy() {
|
||
this._unsetConnections();
|
||
}
|
||
|
||
_unsetConnections() {
|
||
this.settings_connections.forEach((e) => {
|
||
this._settings.disconnect(e);
|
||
});
|
||
}
|
||
|
||
_openPrefs() {
|
||
this._extension.openPreferences();
|
||
}
|
||
|
||
_openMenu() {
|
||
// Grey out menu items if an update is pending
|
||
this.refreshItem.setSensitive(!this._updatePending);
|
||
this.clipboardImageItem.setSensitive(!this._updatePending && this.imageURL != "");
|
||
this.clipboardURLItem.setSensitive(!this._updatePending && this.imageURL != "");
|
||
this.thumbnailItem.setSensitive(!this._updatePending && this.imageURL != "");
|
||
this.dwallpaperItem.setSensitive(!this._updatePending && this.filename != "");
|
||
this.swallpaperItem.setSensitive(!this._updatePending && this.filename != "");
|
||
this.titleItem.setSensitive(!this._updatePending && this.imageinfolink != "");
|
||
let maxlongdate = Utils.getMaxLongDate(this._settings);
|
||
this.refreshduetext =
|
||
_("Next refresh") + ": " + (this.refreshdue ? this.refreshdue.format("%Y-%m-%d %X") : '-') +
|
||
" (" + (this.refreshdue?Utils.friendly_time_diff(this.refreshdue):"-") + ")\n" +
|
||
_("Last refresh") + ": " + (maxlongdate? this._localeDate(maxlongdate, true) : '-');
|
||
// also show when shuffle is next due
|
||
if (this._settings.get_boolean('random-mode-enabled')) {
|
||
this.refreshduetext += "\n" + _("Next shuffle")+": " +
|
||
(this.shuffledue ? this.shuffledue.format("%Y-%m-%d %X") : '-') +
|
||
" (" + (this.refreshdue?Utils.friendly_time_diff(this.shuffledue):"-") + ")";
|
||
}
|
||
BingLog('refreshduetext :'+this.refreshduetext);
|
||
this.refreshDueItem.label.set_text(this.refreshduetext);
|
||
}
|
||
|
||
_setBlur() {
|
||
blur._switch(this._settings.get_boolean('override-lockscreen-blur'));
|
||
blur.set_blur_strength(this._settings.get_int('lockscreen-blur-strength'));
|
||
blur.set_blur_brightness(this._settings.get_int('lockscreen-blur-brightness'));
|
||
}
|
||
|
||
_setImage() {
|
||
Utils.validate_imagename(this._settings);
|
||
this.selected_image = this._settings.get_string('selected-image');
|
||
BingLog('selected image changed to: ' + this.selected_image);
|
||
this._selectImage();
|
||
//this._setShuffleToggleState();
|
||
}
|
||
|
||
_notifyCurrentImage() {
|
||
if (this._settings.get_boolean('notify')) {
|
||
let image = this._getCurrentImage();
|
||
if (image) {
|
||
this._createImageNotification(image);
|
||
}
|
||
}
|
||
}
|
||
|
||
// set indicator icon (tray icon)
|
||
_setIcon() {
|
||
Utils.validate_icon(this._settings, this._extension.path);
|
||
let icon_name = this._settings.get_string('icon-name');
|
||
let gicon = Gio.icon_new_for_string(this._extension.dir.get_child('icons').get_path() + '/' + icon_name + '.svg');
|
||
this.icon = new St.Icon({gicon: gicon, style_class: 'system-status-icon'});
|
||
BingLog('Replace icon set to: ' + icon_name);
|
||
this.remove_all_children();
|
||
this.add_child(this.icon);
|
||
}
|
||
|
||
// set backgrounds as requested and set preview image in menu
|
||
_setBackground() {
|
||
if (this.filename == '')
|
||
return;
|
||
this.thumbnail = new Thumbnail(this.filename, St.ThemeContext.get_for_stage(global.stage).scale_factor); // use scale factor to make them look nicer
|
||
this._setThumbnailImage();
|
||
if (!this.dimensions.width || !this.dimensions.height) // if dimensions aren't in image database yet
|
||
[this.dimensions.width, this.dimensions.height] = Utils.getFileDimensions(this.filename);
|
||
BingLog('image set to : '+this.filename);
|
||
if (this._settings.get_boolean('set-background'))
|
||
this._setBackgroundDesktop();
|
||
}
|
||
|
||
_setBackgroundDesktop() {
|
||
doSetBackground(this.filename, Utils.DESKTOP_SCHEMA);
|
||
}
|
||
|
||
_copyURLToClipboard() {
|
||
this.clipboard.setText(this.imageURL);
|
||
}
|
||
|
||
_copyImageToClipboard() {
|
||
this.clipboard.setImage(this.filename);
|
||
}
|
||
|
||
// set a timer on when the current image is going to expire
|
||
_restartTimeoutFromLongDate(longdate) {
|
||
// all Bing times are in UTC (+0)
|
||
let refreshDue = Utils.dateFromLongDate(longdate, 86400).to_local();
|
||
let now = GLib.DateTime.new_now_local();
|
||
let difference = refreshDue.difference(now) / 1000000;
|
||
|
||
if (difference < 60 || difference > 86400) // clamp to a reasonable range
|
||
difference = 60;
|
||
difference = difference + 300; // 5 minute fudge offset in case of inaccurate local clock
|
||
|
||
BingLog('Next refresh due ' + difference + ' seconds from now');
|
||
this._restartTimeout(difference);
|
||
}
|
||
|
||
// alternative shuffle mode, not yet enabled
|
||
_restartShuffleTimeoutFromDueDate(duedate) {
|
||
let now = GLib.DateTime.new_now_local();
|
||
let difference = duedate.difference(now) / 1000000;
|
||
if (difference < 60 || difference > 86400) // clamp to a reasonable range
|
||
difference = 60;
|
||
|
||
BingLog('Next shuffle due ' + difference + ' seconds from now');
|
||
this._restartShuffleTimeout(difference);
|
||
}
|
||
|
||
// convert longdate format into human friendly format
|
||
_localeDate(longdate, include_time = false) {
|
||
try {
|
||
let date = Utils.dateFromLongDate(longdate, 300); // date at update
|
||
return date.to_local().format('%Y-%m-%d' + (include_time? ' %X' : '')); // ISO 8601 - https://xkcd.com/1179/
|
||
}
|
||
catch (e) {
|
||
return 'none';
|
||
}
|
||
}
|
||
|
||
// set menu text in lieu of a notification/popup
|
||
_setMenuText() {
|
||
this.titleItem.label.set_text(this.title ? this.title : '');
|
||
this.copyrightItem.label.set_text(this.copyright ? this.copyright : '');
|
||
this.imageResolutionItem.label.set_text(this.dimensions.width+'px x '+this.dimensions.height+'px');
|
||
if (this._settings.get_boolean('show-count-in-image-title') && this.explanation) {
|
||
let imageList = JSON.parse(this._settings.get_string('bing-json'));
|
||
if (imageList.length > 0)
|
||
this.explainItem.label.set_text( this.explanation + ' [' + (this.imageIndex + 1) + '/' + imageList.length + ']');
|
||
}
|
||
else {
|
||
this.explainItem.label.set_text(this.explanation ? this.explanation : '');
|
||
}
|
||
this._setFavouriteIcon(this.favourite_status?this.ICON_FAVE_BUTTON:this.ICON_UNFAVE_BUTTON);
|
||
this._setTrashIcon(this.hidden_status?this.ICON_UNTRASH_BUTTON:this.ICON_TRASH_BUTTON);
|
||
}
|
||
|
||
_wrapLabelItem(menuItem) {
|
||
let clutter_text = menuItem.label.get_clutter_text();
|
||
clutter_text.set_line_wrap(true);
|
||
clutter_text.set_ellipsize(0);
|
||
clutter_text.set_max_length(0);
|
||
menuItem.label.set_style('max-width: 420px;');
|
||
}
|
||
|
||
_setControls() {
|
||
this.favouriteBtn = this._newMenuIcon(
|
||
this.favourite_status?this.ICON_FAVE_BUTTON:this.ICON_UNFAVE_BUTTON,
|
||
this.controlItem,
|
||
this._favouriteImage);
|
||
this.trashBtn = this._newMenuIcon(
|
||
this.hidden_status?this.ICON_UNTRASH_BUTTON:this.ICON_TRASH_BUTTON,
|
||
this.controlItem,
|
||
this._trashImage);
|
||
this.prevBtn = this._newMenuIcon(
|
||
ICON_PREVIOUS_BUTTON,
|
||
this.controlItem,
|
||
this._prevImage);
|
||
this.nextBtn = this._newMenuIcon(
|
||
ICON_NEXT_BUTTON,
|
||
this.controlItem,
|
||
this._nextImage);
|
||
this.curBtn = this._newMenuIcon(
|
||
ICON_CURRENT_BUTTON,
|
||
this.controlItem,
|
||
this._curImage);
|
||
this.randomizeBtn = this._newMenuIcon(
|
||
this.ICON_RANDOM,
|
||
this.controlItem,
|
||
this._selectImage,
|
||
null, true);
|
||
}
|
||
|
||
_newMenuIcon(icon_name, parent, fn, position = null, arg = null) {
|
||
let gicon = Gio.icon_new_for_string(icon_name);
|
||
let icon = new St.Icon({
|
||
/*icon_name: icon_name,*/
|
||
gicon: gicon,
|
||
style_class: 'popup-menu-icon',
|
||
x_expand: true,
|
||
y_expand: true,
|
||
icon_size: this._settings.get_int('controls-icon-size')
|
||
});
|
||
|
||
let iconBtn = new St.Button({
|
||
style_class: 'ci-action-btn',
|
||
can_focus: true,
|
||
child: icon,
|
||
/* x_align: Clutter.ActorAlign.END, // FIXME: errors on GNOME 3.28, default to center is ok */
|
||
x_expand: true,
|
||
y_expand: true
|
||
});
|
||
|
||
if (position !== null) {
|
||
parent.insert_child_at_index(iconBtn, position);
|
||
}
|
||
else {
|
||
parent.add_child(iconBtn);
|
||
}
|
||
|
||
iconBtn.connect('button-press-event', fn.bind(this, arg));
|
||
return iconBtn;
|
||
}
|
||
|
||
// set menu thumbnail
|
||
_setThumbnailImage() {
|
||
let pixbuf = this.thumbnail.pixbuf;
|
||
let scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||
|
||
if (pixbuf == null)
|
||
return;
|
||
|
||
const {width, height} = pixbuf;
|
||
|
||
if (height == 0) {
|
||
return;
|
||
}
|
||
|
||
const [version] = Config.PACKAGE_VERSION.split('.').map(s => Number(s));
|
||
const image = new St.ImageContent();
|
||
const success = image.set_data.apply(image, [
|
||
...version >= 48 ? [Clutter.get_default_backend().get_cogl_context()] : [],
|
||
pixbuf.get_pixels(),
|
||
pixbuf.get_has_alpha() ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888,
|
||
width,
|
||
height,
|
||
pixbuf.get_rowstride(),
|
||
]);
|
||
|
||
if (!success) {
|
||
throw Error("error creating St.ImageContent()");
|
||
}
|
||
|
||
this.thumbnailItem.hexpand = false;
|
||
this.thumbnailItem.vexpand = false;
|
||
this.thumbnailItem.content = image;
|
||
|
||
BingLog('scale factor: ' + scale_factor);
|
||
this.thumbnailItem.set_size(480*scale_factor, 270*scale_factor);
|
||
this.thumbnailItem.setSensitive(true);
|
||
}
|
||
|
||
_nextImage() {
|
||
this._gotoImage(1);
|
||
}
|
||
|
||
_prevImage() {
|
||
this._gotoImage(-1);
|
||
}
|
||
|
||
_curImage() {
|
||
this._setStringSetting('selected-image', 'current');
|
||
this._gotoImage(0);
|
||
}
|
||
|
||
_randomModeChanged() {
|
||
let randomEnabled = this._settings.get_boolean('random-mode-enabled');
|
||
Utils.validate_interval(this._settings);
|
||
[this.toggleShuffleOnlyFaves, this.toggleShuffleOnlyUHD /*, this.toggleShuffleOnlyUnhidden*/]
|
||
.forEach( x => {
|
||
x.setSensitive(randomEnabled);
|
||
});
|
||
if (randomEnabled) {
|
||
BingLog('enabled shuffle mode, by setting a shuffe timer (5 seconds)');
|
||
this._restartShuffleTimeout(5);
|
||
this._setBooleanSetting('revert-to-current-image', false);
|
||
}
|
||
else {
|
||
// clear shuffle timer
|
||
if (this._shuffleTimeout)
|
||
GLib.source_remove(this._shuffleTimeout);
|
||
this._setBooleanSetting('revert-to-current-image', true);
|
||
}
|
||
}
|
||
|
||
_favouriteImage() {
|
||
BingLog('favourite image '+this.imageURL+' status was '+this.favourite_status);
|
||
this.favourite_status = !this.favourite_status;
|
||
Utils.setImageFavouriteStatus(this._settings, this.imageURL, this.favourite_status);
|
||
this._setFavouriteIcon(this.favourite_status?this.ICON_FAVE_BUTTON:this.ICON_UNFAVE_BUTTON);
|
||
}
|
||
|
||
_trashImage() {
|
||
BingLog('trash image '+this.imageURL+' status was '+this.hidden_status);
|
||
this.hidden_status = !this.hidden_status;
|
||
Utils.setImageHiddenStatus(this._settings, this.imageURL, this.hidden_status);
|
||
this._setTrashIcon(this.hidden_status?this.ICON_UNTRASH_BUTTON:this.ICON_TRASH_BUTTON);
|
||
if (this._settings.get_boolean('trash-deletes-images')) {
|
||
BingLog('image to be deleted: '+this.filename);
|
||
Utils.deleteImage(this.filename);
|
||
Utils.validate_imagename(this._settings);
|
||
}
|
||
|
||
}
|
||
|
||
_setFavouriteIcon(icon_name) {
|
||
let gicon = Gio.icon_new_for_string(icon_name);
|
||
this.favouriteBtn.get_children().forEach( (x, i) => {
|
||
x.set_gicon(gicon);
|
||
});
|
||
}
|
||
|
||
_setTrashIcon(icon_name) {
|
||
let gicon = Gio.icon_new_for_string(icon_name);
|
||
this.trashBtn.get_children().forEach( (x, i) => {
|
||
x.set_gicon(gicon);
|
||
});
|
||
}
|
||
|
||
_gotoImage(relativePos) {
|
||
let imageList = Utils.getImageList(this._settings);
|
||
let curIndex = 0;
|
||
|
||
if (this.selected_image == 'current') {
|
||
curIndex = Utils.getCurrentImageIndex(imageList);
|
||
}
|
||
else {
|
||
curIndex = Utils.imageIndex(imageList, this.selected_image);
|
||
}
|
||
|
||
let newImage = Utils.getImageByIndex(imageList, curIndex + relativePos);
|
||
|
||
if (newImage)
|
||
this._setStringSetting('selected-image', newImage.urlbase.replace('/th?id=OHR.', ''));
|
||
}
|
||
|
||
_getCurrentImage() {
|
||
let imageList = Utils.getImageList(this._settings);
|
||
let curIndex = Utils.getCurrentImageIndex(imageList);
|
||
|
||
return Utils.getImageByIndex(imageList, curIndex);
|
||
}
|
||
|
||
// download Bing metadata
|
||
_refresh() {
|
||
if (this._updatePending)
|
||
return;
|
||
this._updatePending = true;
|
||
this._restartTimeout();
|
||
|
||
let market = this._settings.get_string('market');
|
||
// Soup3 should be the version used, but in the past some distros have packaged older versions only
|
||
if (Soup.MAJOR_VERSION >= 3) {
|
||
let url = BingImageURL;
|
||
let params = Utils.BingParams;
|
||
params['mkt'] = ( market != 'auto' ? market : '' );
|
||
|
||
// if we've set previous days to be something less than 8 and
|
||
// delete previous is active we want to just request a subset of wallpapers
|
||
if (this._settings.get_boolean('delete-previous') == true && this._settings.get_int('previous-days')<8) {
|
||
params['n'] = ""+this._settings.get_int('previous-days');
|
||
}
|
||
|
||
let request = Soup.Message.new_from_encoded_form('GET', url, Soup.form_encode_hash(params));
|
||
request.request_headers.append('Accept', 'application/json');
|
||
|
||
try {
|
||
this.httpSession.send_and_read_async(request, GLib.PRIORITY_DEFAULT, null, (httpSession, message) => {
|
||
this._processMessageRefresh(message);
|
||
});
|
||
}
|
||
catch(error) {
|
||
BingLog('unable to send libsoup json message '+error);
|
||
notifyError('Unable to fetch Bing metadata\n'+error);
|
||
}
|
||
}
|
||
else {
|
||
let url = BingImageURL + '?format=js&idx=0&n=8&mbl=1&mkt=' + (market != 'auto' ? market : '');
|
||
let request = Soup.Message.new('GET', url);
|
||
request.request_headers.append('Accept', 'application/json');
|
||
|
||
// queue the http request
|
||
try {
|
||
this.httpSession.queue_message(request, (httpSession, message) => {
|
||
this._processMessageRefresh(message);
|
||
});
|
||
}
|
||
catch (error) {
|
||
BingLog('unable to send libsoup json message '+error);
|
||
notifyError('Unable to fetch Bing metadata\n'+error);
|
||
}
|
||
}
|
||
}
|
||
|
||
_processMessageRefresh(message) {
|
||
const decoder = new TextDecoder();
|
||
try {
|
||
let data = (Soup.MAJOR_VERSION >= 3) ?
|
||
decoder.decode(this.httpSession.send_and_read_finish(message).get_data()): // Soup3
|
||
message.response_body.data; // Soup 2
|
||
|
||
BingLog('Recieved ' + data.length + ' bytes');
|
||
this._parseData(data);
|
||
|
||
if (!this._settings.get_boolean('random-mode-enabled'))
|
||
this._selectImage();
|
||
}
|
||
catch (error) {
|
||
BingLog('Network error occured: ' + error);
|
||
notifyError('network error occured\n'+error);
|
||
this._updatePending = false;
|
||
this._restartTimeout(TIMEOUT_SECONDS_ON_HTTP_ERROR);
|
||
}
|
||
}
|
||
|
||
// sets a timer for next refresh of Bing metadata
|
||
_restartTimeout(seconds = null) {
|
||
if (this._timeout)
|
||
GLib.source_remove(this._timeout);
|
||
|
||
if (seconds == null)
|
||
seconds = TIMEOUT_SECONDS;
|
||
|
||
this._timeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, seconds, this._refresh.bind(this));
|
||
this.refreshdue = GLib.DateTime.new_now_local().add_seconds(seconds);
|
||
BingLog('next check in ' + seconds + ' seconds');
|
||
}
|
||
|
||
_restartShuffleTimeout(seconds = null) {
|
||
BingLog('_restartShuffleTimeout('+seconds+')');
|
||
//console.trace();
|
||
|
||
if (this._shuffleTimeout)
|
||
GLib.source_remove(this._shuffleTimeout);
|
||
|
||
if (seconds == null) {
|
||
let diff = -Math.floor(GLib.DateTime.new_now_local().difference(this.shuffledue)/1000000);
|
||
BingLog('shuffle ('+this.shuffledue.format_iso8601()+') diff = '+diff);
|
||
if (diff > 30) { // on occasions the above will be 1 second
|
||
seconds = diff; // if not specified, we should maintain the existing shuffle timeout (i.e. we just restored from saved state)
|
||
}
|
||
else if (this._settings.get_string('random-interval-mode') != 'custom') {
|
||
let random_mode = this._settings.get_string('random-interval-mode');
|
||
seconds = Utils.seconds_until(random_mode); // else we shuffle at specified interval (midnight default)
|
||
BingLog('shuffle mode = '+random_mode+' = '+seconds+' from now');
|
||
}
|
||
else {
|
||
seconds = this._settings.get_int('random-interval'); // or whatever the user has specified (as a timer)
|
||
}
|
||
}
|
||
|
||
this._shuffleTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, seconds, this._selectImage.bind(this, true));
|
||
this.shuffledue = GLib.DateTime.new_now_local().add_seconds(seconds);
|
||
BingLog('next shuffle in ' + seconds + ' seconds');
|
||
}
|
||
|
||
// auto export Bing data to JSON file if requested
|
||
_exportData() {
|
||
if (this._settings.get_boolean('always-export-bing-json')) { // save copy of current JSON
|
||
Utils.exportBingJSON(this._settings);
|
||
}
|
||
}
|
||
|
||
// process Bing metadata
|
||
_parseData(data) {
|
||
try {
|
||
let parsed = JSON.parse(data);
|
||
let datamarket = parsed.market.mkt;
|
||
let prefmarket = this._settings.get_string('market');
|
||
let newImages = Utils.mergeImageLists(this._settings, parsed.images);
|
||
|
||
if (datamarket != prefmarket && prefmarket != 'auto')
|
||
BingLog('WARNING: Bing returning market data for ' + datamarket + ' rather than selected ' + prefmarket);
|
||
|
||
Utils.purgeImages(this._settings); // delete older images if enabled
|
||
//Utils.cleanupImageList(this._settings); // merged into purgeImages
|
||
this._downloadAllImages(); // fetch missing images that are still available
|
||
Utils.populateImageListResolutions(this._settings);
|
||
|
||
if (newImages.length > 0 && this._settings.get_boolean('revert-to-current-image')) {
|
||
// user wants to switch to the new image when it arrives
|
||
this._setStringSetting('selected-image', 'current');
|
||
}
|
||
|
||
if (this._settings.get_boolean('notify')) {
|
||
if (!this._settings.get_boolean('notify-only-latest')) {
|
||
// notify all new images
|
||
newImages.forEach((image) => {
|
||
BingLog('New image to notify: ' + Utils.getImageTitle(image));
|
||
this._createImageNotification(image);
|
||
});
|
||
}
|
||
else {
|
||
// notify only the most recent image
|
||
let last = newImages.pop();
|
||
if (last) {
|
||
BingLog('New image to notify: ' + Utils.getImageTitle(last));
|
||
this._createImageNotification(last);
|
||
}
|
||
}
|
||
}
|
||
|
||
this._restartTimeoutFromLongDate(parsed.images[0].fullstartdate); // timing is set by Bing, and possibly varies by market
|
||
this._updatePending = false;
|
||
}
|
||
catch (error) {
|
||
BingLog('_parseData() failed with error ' + error + ' @ '+error.lineNumber);
|
||
notifyError('Bing metadata parsing error check ' + error + ' @ '+error.lineNumber);
|
||
BingLog(error.stack);
|
||
}
|
||
}
|
||
|
||
_cleanUpImages() {
|
||
if (this._settings.get_boolean('delete-previous')) {
|
||
Utils.purgeImages(this._settings);
|
||
}
|
||
}
|
||
|
||
_createImageNotification(image) {
|
||
let msg = _('Bing Wallpaper of the Day for') + ' ' + this._localeDate(image.fullstartdate);
|
||
let details = Utils.getImageTitle(image);
|
||
this._createNotification(msg, details);
|
||
BingLog('_createImageNotification: '+msg+' details: '+details);
|
||
}
|
||
|
||
_createNotification(msg, details) {
|
||
const systemSource = MessageTray.getSystemSource();
|
||
const bingNotify = new MessageTray.Notification({
|
||
source: systemSource,
|
||
title: msg,
|
||
body: details,
|
||
gicon: new Gio.ThemedIcon({name: 'image-x-generic'}),
|
||
iconName: 'image-x-generic',
|
||
});
|
||
systemSource.addNotification(bingNotify);
|
||
//Main.notify(msg, details);
|
||
BingLog('_createNotification: '+msg+' details: '+details);
|
||
}
|
||
|
||
_shuffleImage() {
|
||
let image = null;
|
||
let imageList = Utils.getImageList(this._settings);
|
||
let filter = { 'faves': this._settings.get_boolean('random-mode-include-only-favourites'),
|
||
'hidden': this._settings.get_boolean('random-mode-include-only-unhidden'),
|
||
'min_height': this._settings.get_boolean('random-mode-include-only-uhd')?this._settings.get_int('min-uhd-height'):false
|
||
};
|
||
let favImageList = Utils.getImageList(this._settings, filter);
|
||
|
||
if (favImageList.length >= MINIMUM_SHUFFLE_IMAGES) { // we have the minimum images to shuffle, if not fall back to shuffle all iamges
|
||
imageList = favImageList;
|
||
}
|
||
else {
|
||
BingLog('not enough filtered images available to shuffle');
|
||
}
|
||
|
||
// shuffle could fail for a number of reasons
|
||
try {
|
||
this.imageIndex = Utils.getRandomInt(imageList.length);
|
||
image = imageList[this.imageIndex];
|
||
|
||
BingLog('shuffled to image '+image.urlbase);
|
||
|
||
return image;
|
||
}
|
||
catch (e) {
|
||
BingLog('shuffle failed '+e);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
_selectImage(force_shuffle = false) {
|
||
let imageList = Utils.getImageList(this._settings);
|
||
let image = null;
|
||
// special values, 'current' is most recent (default mode), 'random' picks one at random, anything else should be filename
|
||
|
||
if (force_shuffle) {
|
||
BingLog('forcing shuffle of image')
|
||
image = this._shuffleImage();
|
||
if (this._settings.get_boolean('random-mode-enabled'))
|
||
this._restartShuffleTimeout();
|
||
}
|
||
|
||
if (!image) {
|
||
if (this.selected_image == 'current') {
|
||
image = Utils.getCurrentImage(imageList);
|
||
this.imageIndex = Utils.getCurrentImageIndex(imageList);
|
||
} else {
|
||
image = Utils.inImageList(imageList, this.selected_image);
|
||
|
||
if (!image) // if we didn't find it, try for current
|
||
image = Utils.getCurrentImage(imageList);
|
||
|
||
if (image)
|
||
this.imageIndex = Utils.imageIndex(imageList, image.urlbase);
|
||
BingLog('_selectImage: ' + this.selected_image + ' = ' + (image && image.urlbase ? image.urlbase : 'not found'));
|
||
}
|
||
}
|
||
|
||
if (!image)
|
||
return; // could force, image = imageList[0] or perhaps force refresh
|
||
|
||
if (image.url != '') {
|
||
let resolution = Utils.getResolution(this._settings, image);
|
||
let BingWallpaperDir = Utils.getWallpaperDir(this._settings);
|
||
|
||
// set current image details at extension scope
|
||
this.title = image.copyright.replace(/\s*[\(\(].*?[\)\)]\s*/g, '');
|
||
this.explanation = _('Bing Wallpaper of the Day for') + ' ' + this._localeDate(image.startdate);
|
||
this.copyright = image.copyright.match(/[\(\(]([^)]+)[\)\)]/)[1].replace('\*\*', ''); // Japan locale uses () rather than ()
|
||
this.longstartdate = image.fullstartdate;
|
||
this.imageinfolink = image.copyrightlink.replace(/^http:\/\//i, 'https://');
|
||
this.imageURL = BingURL + image.urlbase + '_' + resolution + '.jpg'+'&qlt=100'; // generate image url for user's resolution @ high quality
|
||
this.filename = Utils.toFilename(BingWallpaperDir, image.startdate, image.urlbase, resolution);
|
||
this.dimensions.width = image.width?image.width:null;
|
||
this.dimensions.height = image.height?image.height:null;
|
||
this.selected_image = Utils.getImageUrlBase(image);
|
||
this._setStringSetting('selected-image', this.selected_image);
|
||
|
||
if (("favourite" in image) && image.favourite === true ) {
|
||
this.favourite_status = true;
|
||
}
|
||
else {
|
||
this.favourite_status = false;
|
||
}
|
||
|
||
if (("hidden" in image) && image.hidden === true ) {
|
||
this.hidden_status = true;
|
||
}
|
||
else {
|
||
this.hidden_status = false;
|
||
}
|
||
|
||
let file = Gio.file_new_for_path(this.filename);
|
||
let file_exists = file.query_exists(null);
|
||
let file_info = file_exists ? file.query_info ('*', Gio.FileQueryInfoFlags.NONE, null) : 0;
|
||
|
||
if (!file_exists || file_info.get_size () == 0) { // file doesn't exist or is empty (probably due to a network error)
|
||
this._downloadImage(this.imageURL, file, true);
|
||
}
|
||
else {
|
||
this._setBackground();
|
||
this._updatePending = false;
|
||
}
|
||
}
|
||
else {
|
||
this.title = _("No wallpaper available");
|
||
this.explanation = _("No picture for today.");
|
||
this.filename = "";
|
||
this._updatePending = false;
|
||
}
|
||
|
||
this._setMenuText();
|
||
this._storeState();
|
||
}
|
||
|
||
_imageURL(urlbase, resolution) {
|
||
return BingURL + urlbase + '_' + resolution + '.jpg';
|
||
}
|
||
|
||
_storeState() {
|
||
if (this.filename) {
|
||
let maxLongDate = Utils.getMaxLongDate(this._settings); // refresh date from most recent Bing image
|
||
let state = {maxlongdate: maxLongDate, title: this.title, explanation: this.explanation, copyright: this.copyright,
|
||
longstartdate: this.longstartdate, imageinfolink: this.imageinfolink, imageURL: this.imageURL,
|
||
filename: this.filename, favourite: this.favourite_status, width: this.dimensions.width,
|
||
height: this.dimensions.height,
|
||
shuffledue: (this.shuffledue.to_unix? this.shuffledue.to_unix():0)
|
||
};
|
||
let stateJSON = JSON.stringify(state);
|
||
|
||
BingLog('Storing state as JSON: ' + stateJSON);
|
||
this._setStringSetting('state', stateJSON);
|
||
}
|
||
}
|
||
|
||
_reStoreState() {
|
||
try {
|
||
// patch for relative paths, ensures that users running git version don't end up with broken state - see EGO review for version 38 https://extensions.gnome.org/review/30299
|
||
this._setStringSetting('download-folder', this._settings.get_string('download-folder').replace('$HOME', '~'));
|
||
let stateJSON = this._settings.get_string('state');
|
||
let state = JSON.parse(stateJSON);
|
||
let maxLongDate = null;
|
||
|
||
BingLog('restoring state...');
|
||
maxLongDate = state.maxlongdate ? state.maxlongdate : null;
|
||
this.title = state.title;
|
||
this.explanation = state.explanation;
|
||
this.copyright = state.copyright;
|
||
this.longstartdate = state.longstartdate;
|
||
this.imageinfolink = state.imageinfolink;
|
||
this.imageURL = state.imageURL;
|
||
this.filename = state.filename;
|
||
this.dimensions.width = state.width;
|
||
this.dimensions.height = state.height;
|
||
this._selected_image = this._settings.get_string('selected-image');
|
||
this.shuffledue = ("shuffledue" in state)? GLib.DateTime.new_from_unix_local(state.shuffledue) : 0;
|
||
this.favourite_status = ("favourite" in state && state.favourite === true);
|
||
// update menus and thumbnail
|
||
this._setMenuText();
|
||
this._setBackground();
|
||
|
||
if (!maxLongDate) {
|
||
this._restartTimeout(60);
|
||
return;
|
||
}
|
||
|
||
if (this._settings.get_boolean('random-mode-enabled')) {
|
||
BingLog('random mode enabled, restarting random state');
|
||
this._restartShuffleTimeoutFromDueDate(this.shuffledue); // FIXME: use state value
|
||
this._restartTimeoutFromLongDate(maxLongDate);
|
||
}
|
||
else {
|
||
this._restartTimeoutFromLongDate(maxLongDate);
|
||
}
|
||
|
||
return;
|
||
}
|
||
catch (error) {
|
||
BingLog('bad state - refreshing... error was ' + error);
|
||
}
|
||
this._restartTimeout(60);
|
||
}
|
||
|
||
_downloadAllImages() {
|
||
// fetch recent undownloaded images
|
||
let imageList = Utils.getFetchableImageList(this._settings);
|
||
let BingWallpaperDir = Utils.getWallpaperDir(this._settings);
|
||
imageList.forEach( (image) => {
|
||
let resolution = Utils.getResolution(this._settings, image);
|
||
let filename = Utils.toFilename(BingWallpaperDir, image.startdate, image.urlbase, resolution);
|
||
let url = this._imageURL(image.urlbase, resolution);
|
||
let file = Gio.file_new_for_path(filename);
|
||
this._downloadImage(url, file, false);
|
||
});
|
||
}
|
||
|
||
// download and process new image
|
||
// FIXME: improve error handling
|
||
_downloadImage(url, file, set_background) {
|
||
let BingWallpaperDir = Utils.getWallpaperDir(this._settings);
|
||
let dir = Gio.file_new_for_path(BingWallpaperDir);
|
||
if (!dir.query_exists(null)) {
|
||
//dir.make_directory_with_parents(null);
|
||
notifyError('Download folder '+BingWallpaperDir+' does not exist or is not writable');
|
||
return;
|
||
}
|
||
BingLog("Downloading " + url + " to " + file.get_uri());
|
||
let request = Soup.Message.new('GET', url);
|
||
|
||
// queue the http request
|
||
try {
|
||
if (Soup.MAJOR_VERSION >= 3) {
|
||
this.httpSession.send_and_read_async(request, GLib.PRIORITY_DEFAULT, null, (httpSession, message) => {
|
||
this._processFileDownload(message, file, set_background);
|
||
});
|
||
}
|
||
else {
|
||
this.httpSession.queue_message(request, (httpSession, message) => {
|
||
this._processFileDownload(message, file, set_background);
|
||
});
|
||
}
|
||
}
|
||
catch (error) {
|
||
BingLog('error sending libsoup message '+error);
|
||
notifyError('Network error '+error);
|
||
}
|
||
}
|
||
|
||
_processFileDownload(message, file, set_background) {
|
||
try {
|
||
let data = (Soup.MAJOR_VERSION >= 3) ?
|
||
this.httpSession.send_and_read_finish(message).get_data():
|
||
message.response_body.flatten().get_as_bytes();
|
||
|
||
file.replace_contents_bytes_async(
|
||
data,
|
||
null,
|
||
false,
|
||
Gio.FileCreateFlags.REPLACE_DESTINATION,
|
||
null,
|
||
(file, res) => {
|
||
try {
|
||
file.replace_contents_finish(res);
|
||
if (set_background)
|
||
this._setBackground();
|
||
BingLog('Download successful');
|
||
}
|
||
catch(e) {
|
||
BingLog('Error writing file: ' + e);
|
||
notifyError('Image '+file.get_path()+' is not writable, check folder permissions or select a different folder\n'+e);
|
||
}
|
||
}
|
||
);
|
||
}
|
||
catch (error) {
|
||
BingLog('Unable download image '+error);
|
||
notifyError('Image '+file.get_path()+' file error, check folder permissions, disk space or select a different folder\n'+e);
|
||
}
|
||
}
|
||
|
||
// open image in default image view
|
||
_openInSystemViewer() {
|
||
Utils.openInSystemViewer(this.filename);
|
||
}
|
||
|
||
// open Bing image information page
|
||
_openImageInfoLink() {
|
||
if (this.imageinfolink)
|
||
Utils.openInSystemViewer(this.imageinfolink, false);
|
||
}
|
||
|
||
stop() {
|
||
if (this._timeout)
|
||
GLib.source_remove(this._timeout);
|
||
|
||
if (this._shuffleTimeout)
|
||
GLib.source_remove(this._shuffleTimeout);
|
||
|
||
this._timeout = undefined;
|
||
this._shuffleTimeout = undefined;
|
||
this.menu.removeAll();
|
||
blur._disable(); // disable blur (blur.js) override and cleanup
|
||
blur = null;
|
||
}
|
||
});
|
||
|
||
export default class BingWallpaperExtension extends Extension {
|
||
enable() {
|
||
bingWallpaperIndicator = new BingWallpaperIndicator(this);
|
||
Main.panel.addToStatusArea(IndicatorName, bingWallpaperIndicator);
|
||
}
|
||
disable() {
|
||
bingWallpaperIndicator.stop();
|
||
bingWallpaperIndicator.destroy();
|
||
bingWallpaperIndicator = null;
|
||
}
|
||
}
|
||
|
||
function toFilename(wallpaperDir, startdate, imageURL, resolution) {
|
||
return wallpaperDir + startdate + '-' + imageURL.replace(/^.*[\\\/]/, '').replace('th?id=OHR.', '') + '_' + resolution + '.jpg';
|
||
}
|
||
|