utils.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. // Bing Wallpaper GNOME extension
  2. // Copyright (C) 2017-2023 Michael Carroll
  3. // This extension is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Lesser General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. // See the GNU General Public License, version 3 or later for details.
  8. // Based on GNOME shell extension NASA APOD by Elia Argentieri https://github.com/Elinvention/gnome-shell-extension-nasa-apod
  9. import Gio from 'gi://Gio';
  10. import GLib from 'gi://GLib';
  11. import Soup from 'gi://Soup';
  12. import GdkPixbuf from 'gi://GdkPixbuf';
  13. export var PRESET_GNOME_DEFAULT = { blur: 60, dim: 55 }; // as at GNOME 40
  14. export var PRESET_NO_BLUR = { blur: 0, dim: 60 };
  15. export var PRESET_SLIGHT_BLUR = { blur: 2, dim: 60 };
  16. export var BING_SCHEMA = 'org.gnome.shell.extensions.bingwallpaper';
  17. export var DESKTOP_SCHEMA = 'org.gnome.desktop.background';
  18. var vertical_blur = null;
  19. var horizontal_blur = null;
  20. let gitreleaseurl = 'https://api.github.com/repos/neffo/bing-wallpaper-gnome-extension/releases/tags/';
  21. let debug = false;
  22. export var icon_list = ['bing-symbolic', 'brick-symbolic', 'high-frame-symbolic', 'mid-frame-symbolic', 'low-frame-symbolic'];
  23. export var resolutions = ['auto', 'UHD', '1920x1200', '1920x1080', '1366x768', '1280x720', '1024x768', '800x600'];
  24. export var markets = ['auto', 'ar-XA', 'da-DK', 'de-AT', 'de-CH', 'de-DE', 'en-AU', 'en-CA', 'en-GB',
  25. 'en-ID', 'en-IE', 'en-IN', 'en-MY', 'en-NZ', 'en-PH', 'en-SG', 'en-US', 'en-WW', 'en-XA', 'en-ZA', 'es-AR',
  26. 'es-CL', 'es-ES', 'es-MX', 'es-US', 'es-XL', 'et-EE', 'fi-FI', 'fr-BE', 'fr-CA', 'fr-CH', 'fr-FR',
  27. 'he-IL', 'hr-HR', 'hu-HU', 'it-IT', 'ja-JP', 'ko-KR', 'lt-LT', 'lv-LV', 'nb-NO', 'nl-BE', 'nl-NL',
  28. 'pl-PL', 'pt-BR', 'pt-PT', 'ro-RO', 'ru-RU', 'sk-SK', 'sl-SL', 'sv-SE', 'th-TH', 'tr-TR', 'uk-UA',
  29. 'zh-CN', 'zh-HK', 'zh-TW'];
  30. export var marketName = [
  31. 'auto', '(شبه الجزيرة العربية‎) العربية', 'dansk (Danmark)', 'Deutsch (Österreich)',
  32. 'Deutsch (Schweiz)', 'Deutsch (Deutschland)', 'English (Australia)', 'English (Canada)',
  33. 'English (United Kingdom)', 'English (Indonesia)', 'English (Ireland)', 'English (India)', 'English (Malaysia)',
  34. 'English (New Zealand)', 'English (Philippines)', 'English (Singapore)', 'English (United States)',
  35. 'English (International)', 'English (Arabia)', 'English (South Africa)', 'español (Argentina)', 'español (Chile)',
  36. 'español (España)', 'español (México)', 'español (Estados Unidos)', 'español (Latinoamérica)', 'eesti (Eesti)',
  37. 'suomi (Suomi)', 'français (Belgique)', 'français (Canada)', 'français (Suisse)', 'français (France)',
  38. '(עברית (ישראל', 'hrvatski (Hrvatska)', 'magyar (Magyarország)', 'italiano (Italia)', '日本語 (日本)', '한국어(대한민국)',
  39. 'lietuvių (Lietuva)', 'latviešu (Latvija)', 'norsk bokmål (Norge)', 'Nederlands (België)', 'Nederlands (Nederland)',
  40. 'polski (Polska)', 'português (Brasil)', 'português (Portugal)', 'română (România)', 'русский (Россия)',
  41. 'slovenčina (Slovensko)', 'slovenščina (Slovenija)', 'svenska (Sverige)', 'ไทย (ไทย)', 'Türkçe (Türkiye)',
  42. 'українська (Україна)', '中文(中国)', '中文(中國香港特別行政區)', '中文(台灣)'
  43. ];
  44. export var backgroundStyle = ['none', 'wallpaper', 'centered', 'scaled', 'stretched', 'zoom', 'spanned']; // this may change in the future
  45. export var randomIntervals = [ {value: 'hourly', title: ('on the hour')},
  46. {value: 'daily', title: ('every day at midnight')},
  47. {value: 'weekly', title: ('every Sunday at midnight')} ];
  48. export var BingImageURL = 'https://www.bing.com/HPImageArchive.aspx';
  49. export var BingParams = { format: 'js', idx: '0' , n: '8' , mbl: '1' , mkt: '' } ;
  50. export function validate_icon(settings, extension_path, icon_image = null) {
  51. BingLog('validate_icon()');
  52. let icon_name = settings.get_string('icon-name');
  53. if (icon_name == '' || icon_list.indexOf(icon_name) == -1) {
  54. settings.reset('icon-name');
  55. icon_name = settings.get_string('icon-name');
  56. }
  57. // if called from prefs
  58. if (icon_image) {
  59. BingLog('set icon to: ' + extension_path + '/icons/' + icon_name + '.svg');
  60. let pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(extension_path + '/icons/' + icon_name + '.svg', 32, 32);
  61. icon_image.set_from_pixbuf(pixbuf);
  62. }
  63. }
  64. export function validate_resolution(settings) {
  65. let resolution = settings.get_string('resolution');
  66. if (resolution == '' || resolutions.indexOf(resolution) == -1) // if not a valid resolution
  67. settings.reset('resolution');
  68. }
  69. // FIXME: needs work
  70. export function validate_imagename(settings) {
  71. let filename = settings.get_string('selected-image');
  72. if (filename != 'current' || filename != 'random') // FIXME: remove this when we move to new shuffle mode
  73. return;
  74. if (!inImageList(getImageList(settings), filename)) {
  75. BingLog('invalid image selected');
  76. //settings.reset('selected-image');
  77. settings.set_string('selected-image', 'current');
  78. }
  79. }
  80. export function get_current_bg(schema) {
  81. let gsettings = new Gio.Settings({ schema: schema });
  82. let cur = gsettings.get_string('picture-uri');
  83. return (cur);
  84. }
  85. export function fetch_change_log(version, label, httpSession) {
  86. const decoder = new TextDecoder();
  87. // create an http message
  88. let url = gitreleaseurl + "v" + version;
  89. let request = Soup.Message.new('GET', url);
  90. request.request_headers.append('Accept', 'application/json');
  91. BingLog("Fetching " + url);
  92. // queue the http request
  93. try {
  94. if (Soup.MAJOR_VERSION >= 3) {
  95. httpSession.send_and_read_async(request, GLib.PRIORITY_DEFAULT, null, (httpSession, message) => {
  96. let data = decoder.decode(httpSession.send_and_read_finish(message).get_data());
  97. let text = JSON.parse(data).body;
  98. label.set_label(text);
  99. });
  100. }
  101. else {
  102. httpSession.queue_message(request, (httpSession, message) => {
  103. let data = message.response_body.data;
  104. let text = JSON.parse(data).body;
  105. label.set_label(text);
  106. });
  107. }
  108. }
  109. catch (error) {
  110. BingLog("Error fetching change log: " + error);
  111. label.set_label(_("Error fetching change log: "+error));
  112. }
  113. }
  114. export function set_blur_preset(settings, preset) {
  115. settings.set_int('lockscreen-blur-strength', preset.blur);
  116. settings.set_int('lockscreen-blur-brightness', preset.dim);
  117. BingLog("Set blur preset to " + preset.blur + " brightness to " + preset.dim);
  118. }
  119. export function imageHasBasename(image_item, i, b) {
  120. //log("imageHasBasename : " + image_item.urlbase + " =? " + this);
  121. if (this && this.search(image_item.urlbase.replace('th?id=OHR.', '')))
  122. return true;
  123. return false;
  124. }
  125. export function dateFromLongDate(longdate, add_seconds) {
  126. if (typeof longdate === 'number')
  127. longdate = longdate.toString();
  128. return GLib.DateTime.new(GLib.TimeZone.new_utc(),
  129. parseInt(longdate.substr(0, 4)), // year
  130. parseInt(longdate.substr(4, 2)), // month
  131. parseInt(longdate.substr(6, 2)), // day
  132. parseInt(longdate.substr(8, 2)), // hour
  133. parseInt(longdate.substr(10, 2)), // mins
  134. 0 ).add_seconds(add_seconds); // seconds
  135. }
  136. export function dateFromShortDate(shortdate) {
  137. if (typeof shortdate === 'number')
  138. shortdate = shortdate.toString();
  139. return GLib.DateTime.new(GLib.TimeZone.new_utc(),
  140. parseInt(shortdate.substr(0, 4)), // year
  141. parseInt(shortdate.substr(4, 2)), // month
  142. parseInt(shortdate.substr(6, 2)), // day
  143. 0, 0, 0 );
  144. }
  145. export function getImageList(settings, filter = null) {
  146. let image_list = JSON.parse(settings.get_string('bing-json'));
  147. if (!filter) {
  148. return image_list;
  149. }
  150. else {
  151. return image_list.filter((x, i) => {
  152. if (filter.faves && !x.favourite)
  153. return false;
  154. if (filter.min_height && x.height < filter.min_height)
  155. return false;
  156. if (filter.hidden && x.hidden)
  157. return false;
  158. return true;
  159. });
  160. }
  161. }
  162. export function setImageList(settings, imageList) {
  163. settings.set_string('bing-json', JSON.stringify(imageList));
  164. if (settings.get_boolean('always-export-bing-json')) { // save copy of current JSON
  165. exportBingJSON(settings);
  166. }
  167. }
  168. export function setImageHiddenStatus(settings, hide_image_list, hide_status) {
  169. // get current image list
  170. let image_list = getImageList(settings);
  171. image_list.forEach( (x, i) => {
  172. hide_image_list.forEach(u => {
  173. if (u.includes(x.urlbase)) {
  174. // mark as hidden
  175. x.hidden = hide_status;
  176. }
  177. });
  178. });
  179. // export image list back to settings
  180. setImageList(settings, image_list);
  181. }
  182. export function getImageTitle(image_data) {
  183. return image_data.copyright.replace(/\s*\(.*?\)\s*/g, '');
  184. }
  185. export function getImageUrlBase(image_data) {
  186. return image_data.urlbase.replace('/th?id=OHR.', '');
  187. }
  188. export function getMaxLongDate(settings) {
  189. let imageList = getImageList(settings);
  190. return Math.max.apply(Math, imageList.map(function(o) { return o.fullstartdate; }));
  191. }
  192. export function getCurrentImageIndex (imageList) {
  193. if (!imageList)
  194. return -1;
  195. let maxLongDate = Math.max.apply(Math, imageList.map(function(o) { return o.fullstartdate; }));
  196. let index = imageList.map(p => parseInt(p.fullstartdate)).indexOf(maxLongDate);
  197. BingLog('getCurrentImageIndex for ' + maxLongDate + ': ' + index);
  198. return index;
  199. }
  200. export function setImageFavouriteStatus(settings, imageURL, newState) {
  201. BingLog('set favourite status of '+imageURL+' to '+newState);
  202. let imageList = getImageList(settings);
  203. imageList.forEach(function(x, i) {
  204. //log('testing: '+imageURL+' includes '+x.urlbase);
  205. if (imageURL.includes(x.urlbase)) {
  206. BingLog('setting index '+i+' to '+newState?'true':'false');
  207. imageList[i].favourite = newState;
  208. }
  209. });
  210. setImageList(settings, imageList); // save back to settings
  211. }
  212. export function getCurrentImage(imageList) {
  213. if (!imageList || imageList.length == 0)
  214. return null;
  215. let index = getCurrentImageIndex(imageList);
  216. if (index == -1)
  217. return imageList[0]; // give something sensible
  218. return imageList[index];
  219. }
  220. export function inImageList(imageList, urlbase) {
  221. let image = null;
  222. imageList.forEach(function(x, i) {
  223. if (urlbase.replace('/th?id=OHR.', '') == x.urlbase.replace('/th?id=OHR.', ''))
  224. image = x;
  225. });
  226. return image;
  227. }
  228. export function inImageListByTitle(imageList, title) {
  229. let image = null;
  230. imageList.forEach(function(x, i) {
  231. BingLog('inImageListbyTitle(): ' + title + ' == ' + getImageTitle(x));
  232. if (getImageTitle(x) == title)
  233. image = x;
  234. });
  235. return image;
  236. }
  237. export function mergeImageLists(settings, imageList) {
  238. let curList = getImageList(settings);
  239. let newList = []; // list of only new images (for future notifications)
  240. imageList.forEach(function(x, i) {
  241. if (!inImageList(curList, x.urlbase)) {// if not in the list, add it
  242. curList.unshift(x); // use unshift to maintain reverse chronological order
  243. newList.unshift(x);
  244. }
  245. });
  246. setImageList(settings, imageListSortByDate(curList)); // sort then save back to settings
  247. return newList; // return this to caller for notifications
  248. }
  249. export function imageIndex(imageList, urlbase) {
  250. return imageList.map(p => p.urlbase.replace('/th?id=OHR.', '')).indexOf(urlbase.replace('/th?id=OHR.', ''));
  251. }
  252. export function isFavourite(image) {
  253. return (image.favourite && image.favourite === true);
  254. }
  255. export function getImageByIndex(imageList, index) {
  256. if (imageList.length == 0 || index < 0 || index > imageList.length - 1)
  257. return null;
  258. return imageList[index];
  259. }
  260. export function cleanupImageList(settings) {
  261. let curList = imageListSortByDate(getImageList(settings));
  262. let cutOff = GLib.DateTime.new_now_utc().add_days(-8); // 8 days ago
  263. let newList = [];
  264. curList.forEach( function (x, i) {
  265. let filename = imageToFilename(settings, x);
  266. let diff = dateFromLongDate(x.fullstartdate, 0).difference(cutOff);
  267. // image is still downloadable (< 8 days old) or still on disk, so we keep
  268. if (diff > 0 || Gio.file_new_for_path(filename).query_exists(null)) {
  269. newList.push(x);
  270. }
  271. else {
  272. BingLog('Cleaning up: '+filename);
  273. }
  274. });
  275. setImageList(settings, newList);
  276. }
  277. export function populateImageListResolutions(settings) {
  278. let curList = imageListSortByDate(getImageList(settings));
  279. let newList = [];
  280. curList.forEach( function (x, i) {
  281. let filename = imageToFilename(settings, x);
  282. let width, height;
  283. if (!x.width || !x.height) {
  284. [width, height] = getFileDimensions(filename);
  285. x.width = width;
  286. x.height = height;
  287. }
  288. newList.push(x);
  289. });
  290. setImageList(settings, newList);
  291. }
  292. export function getFetchableImageList(settings) {
  293. let imageList = getImageList(settings);
  294. let cutOff = GLib.DateTime.new_now_utc().add_days(-8); // 8 days ago
  295. let dlList = [];
  296. imageList.forEach( function (x, i) {
  297. let diff = dateFromLongDate(x.fullstartdate, 0).difference(cutOff);
  298. let filename = imageToFilename(settings, x);
  299. // image is still downloadable (< 8 days old) but not on disk
  300. if (diff > 0 && !Gio.file_new_for_path(filename).query_exists(null)) {
  301. dlList.push(x);
  302. }
  303. });
  304. return dlList;
  305. }
  306. export function getWallpaperDir(settings) {
  307. let homeDir = GLib.get_home_dir();
  308. let BingWallpaperDir = settings.get_string('download-folder').replace('~', homeDir);
  309. let userPicturesDir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES);
  310. let userDesktopDir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP); // seems to be a safer default
  311. if (BingWallpaperDir == '') {
  312. BingWallpaperDir = (userPicturesDir?userPicturesDir:userDesktopDir) + '/BingWallpaper/';
  313. BingLog('Using default download folder: ' + BingWallpaperDir);
  314. setWallpaperDir(settings, BingWallpaperDir);
  315. }
  316. else if (!BingWallpaperDir.endsWith('/')) {
  317. BingWallpaperDir += '/';
  318. }
  319. let dir = Gio.file_new_for_path(BingWallpaperDir);
  320. if (!dir.query_exists(null)) {
  321. dir.make_directory_with_parents(null);
  322. }
  323. //FIXME: test if dir is good and writable
  324. return BingWallpaperDir;
  325. }
  326. export function setWallpaperDir(settings, uri) {
  327. let homeDir = GLib.get_home_dir();
  328. let relUri = uri.replace(homeDir, '~');
  329. settings.set_string('download-folder', relUri);
  330. }
  331. export function imageToFilename(settings, image, resolution = null) {
  332. return getWallpaperDir(settings) + image.startdate + '-' +
  333. image.urlbase.replace(/^.*[\\\/]/, '').replace('th?id=OHR.', '') + '_'
  334. + (resolution ? resolution : getResolution(settings, image)) + '.jpg';
  335. }
  336. export function getRandomInt(max) {
  337. return Math.floor(Math.random() * max);
  338. }
  339. // Utility function
  340. export function dump(object, level = 0) {
  341. let output = '';
  342. for (let property in object) {
  343. output += ' - '.repeat(level)+property + ': ' + object[property]+'\n ';
  344. if ( typeof object[property] === 'object' )
  345. output += dump(object[property], level+1);
  346. }
  347. if (level == 0)
  348. BingLog(output);
  349. return(output);
  350. }
  351. export function friendly_time_diff(time, short = true) {
  352. // short we want to keep ~4-5 characters
  353. let now = GLib.DateTime.new_now_local().to_unix();
  354. let seconds = time.to_unix() - now;
  355. if (seconds <= 0) {
  356. return "now";
  357. }
  358. else if (seconds < 60) {
  359. return "< 1 " + (short ? "m" : _("minutes"));
  360. }
  361. else if (seconds < 3600) {
  362. return Math.round(seconds / 60) + " " + (short ? "m" : _("minutes"));
  363. }
  364. else if (seconds > 86400) {
  365. return Math.round(seconds / 86400) + " " + (short ? "d" : _("days"));
  366. }
  367. else {
  368. return Math.round(seconds / 3600) + " " + (short ? "h" : _("hours"));
  369. }
  370. }
  371. export function seconds_until(until) {
  372. let now = GLib.DateTime.new_now_local();
  373. let end, day;
  374. if (until == 'hourly') {
  375. end = GLib.DateTime.new_local(
  376. now.get_year(),
  377. now.get_month(),
  378. now.get_day_of_month(),
  379. now.get_hour()+1, // should roll over to next day if results in >23
  380. 0, 0);
  381. }
  382. else {
  383. if (until == 'weekly') {
  384. day = now.add_days(7 - now.get_day_of_week());
  385. }
  386. else {
  387. day = now.add_days(1);
  388. }
  389. end = GLib.DateTime.new_local(
  390. day.get_year(),
  391. day.get_month(),
  392. day.get_day_of_month(),
  393. 0, 0, 0); // midnight
  394. }
  395. BingLog('shuffle timer will be set to '+end.format_iso8601());
  396. return(Math.floor(end.difference(now)/1000000)); // difference in μs -> s
  397. }
  398. export function getResolution(settings, image) {
  399. let resolution = settings.get_string('resolution');
  400. if (resolutions.indexOf(resolution) == -1 || (image ? image.wp == false : true) || // wp == false when background is animated
  401. settings.get_string('resolution') == 'auto' ) {
  402. // resolution invalid, animated background or autoselected
  403. resolution = 'UHD';
  404. }
  405. return resolution;
  406. }
  407. export function openImageFolder(settings) {
  408. //const context = global?global.create_app_launch_context(0, -1):null;
  409. Gio.AppInfo.launch_default_for_uri('file://' + getWallpaperDir(settings), null);
  410. }
  411. export function imageListSortByDate(imageList) {
  412. return imageList.sort(function(a, b) {
  413. var x = parseInt(a.fullstartdate); var y = parseInt(b.fullstartdate);
  414. return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  415. });
  416. }
  417. export function shortenName(string, limit) {
  418. if (string.length > limit) {
  419. string = string.substr(0, limit - 4) + '...';
  420. }
  421. return string;
  422. }
  423. export function moveImagesToNewFolder(settings, oldPath, newPath) {
  424. // possible race condition here, need to think about how to fix it
  425. //let BingWallpaperDir = settings.get_string('download-folder');
  426. let dir = Gio.file_new_for_path(oldPath);
  427. let dirIter = dir.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null );
  428. let newDir = Gio.file_new_for_path(newPath);
  429. if (!newDir.query_exists(null)) {
  430. newDir.make_directory_with_parents(null);
  431. }
  432. let file = null;
  433. while (file = dirIter.next_file(null)) {
  434. let filename = file.get_name(); // we only want to move files that we think we own
  435. if (filename.match(/\d{8}\-.+\.jpg/i)) {
  436. BingLog('file: ' + slash(oldPath) + filename + ' -> ' + slash(newPath) + filename);
  437. let cur = Gio.file_new_for_path(slash(oldPath) + filename);
  438. let dest = Gio.file_new_for_path(slash(newPath) + filename);
  439. cur.move(dest, Gio.FileCopyFlags.OVERWRITE, null, function () { BingLog ('...moved'); });
  440. }
  441. }
  442. // correct filenames for GNOME backgrounds
  443. if (settings.get_boolean('set-background'))
  444. moveBackground(oldPath, newPath, DESKTOP_SCHEMA);
  445. }
  446. export function dirname(path) {
  447. return path.match(/.*\//);
  448. }
  449. export function slash(path) {
  450. if (!path.endsWith('/'))
  451. return path += '/';
  452. return path;
  453. }
  454. export function moveBackground(oldPath, newPath, schema) {
  455. let gsettings = new Gio.Settings({schema: schema});
  456. let uri = gsettings.get_string('picture-uri');
  457. gsettings.set_string('picture-uri', uri.replace(oldPath, newPath));
  458. try {
  459. let dark_uri = gsettings.get_string('picture-uri-dark');
  460. gsettings.set_string('picture-uri-dark', dark_uri.replace(oldPath, newPath));
  461. }
  462. catch (e) {
  463. BingLog('no dark background gsettings key found ('+e+')');
  464. }
  465. Gio.Settings.sync();
  466. gsettings.apply();
  467. }
  468. export function BingLog(msg) {
  469. if (debug)
  470. print("BingWallpaper extension: " + msg); // disable to keep the noise down in journal
  471. }
  472. export function deleteImage(to_delete) {
  473. var file = Gio.file_new_for_path(to_delete);
  474. if (file.query_exists(null)) {
  475. try {
  476. file.delete(null);
  477. BingLog("deleted file: " + to_delete);
  478. }
  479. catch (error) {
  480. BingLog("an error occured deleting " + to_delete + " : " + error);
  481. }
  482. }
  483. }
  484. // add image to persistant list so we can delete it later (in chronological order), delete the oldest image (if user wants this)
  485. export function purgeImages(settings) {
  486. let deletepictures = settings.get_boolean('delete-previous');
  487. if (deletepictures === false)
  488. return;
  489. let imagelist = imageListSortByDate(getImageList(settings));
  490. let maxpictures = settings.get_int('previous-days');
  491. let origlength = imagelist.length;
  492. while (imagelist.length > maxpictures) {
  493. var to_delete = imagelist.shift(); // get the first (oldest item from the list)
  494. if (deletepictures && to_delete != '') {
  495. let imageFilename = imageToFilename(settings, to_delete);
  496. BingLog('deleting '+imageFilename);
  497. deleteImage(imageFilename);
  498. }
  499. }
  500. BingLog('cleaned up image list, count was '+origlength+' now '+imagelist.length);
  501. cleanupImageList(settings);
  502. validate_imagename(settings); // if we deleted our current image, we want to reset it to something valid
  503. }
  504. export function openInSystemViewer(filename, is_file = true) {
  505. let context;
  506. try {
  507. context = global.create_app_launch_context(0, -1);
  508. }
  509. catch (error) {
  510. context = null;
  511. }
  512. if (is_file)
  513. filename = 'file://'+filename;
  514. Gio.AppInfo.launch_default_for_uri(filename, context);
  515. }
  516. export function exportBingJSON(settings) {
  517. let json = settings.get_string('bing-json');
  518. let filepath = getWallpaperDir(settings) + 'bing.json';
  519. let file = Gio.file_new_for_path(filepath);
  520. let [success, error] = file.replace_contents(json, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null);
  521. if (!success) {
  522. BingLog('error saving bing-json from '+filepath+': '+error);
  523. }
  524. }
  525. export function importBingJSON(settings) {
  526. const decoder = new TextDecoder();
  527. let filepath = getWallpaperDir(settings) + 'bing.json';
  528. let file = Gio.file_new_for_path(filepath);
  529. if (file.query_exists(null)) {
  530. let [success, contents, etag_out] = file.load_contents(null);
  531. if (!success) {
  532. BingLog('error loading bing-json '+filepath+' - '+etag_out);
  533. }
  534. else {
  535. BingLog('JSON import success');
  536. let parsed = JSON.parse(decoder.decode(contents)); // FIXME: triggers GJS warning without the conversion, need to investigate
  537. // need to implement some checks for validity here
  538. mergeImageLists(settings, parsed);
  539. cleanupImageList(settings); // remove the older missing images
  540. }
  541. }
  542. else {
  543. BingLog('JSON import file not found');
  544. }
  545. }
  546. export function getFileDimensions(filepath) {
  547. let format, width, height;
  548. try {
  549. [format, width, height] = GdkPixbuf.Pixbuf.get_file_info(filepath);
  550. return [width, height];
  551. }
  552. catch (e) {
  553. BingLog('unable to getFileDimensions('+filepath+') '+e);
  554. return [null, null];
  555. }
  556. }
  557. export function toFilename(wallpaperDir, startdate, imageURL, resolution) {
  558. return wallpaperDir + startdate + '-' + imageURL.replace(/^.*[\\\/]/, '').replace('th?id=OHR.', '') + '_' + resolution + '.jpg';
  559. }