-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
1,058 additions
and
829 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
const Gio = imports.gi.Gio; | ||
|
||
const Self = imports.misc.extensionUtils.getCurrentExtension(); | ||
const LoggerModule = Self.imports.logger; | ||
|
||
/* | ||
libSoup is accessed through the SoupBowl wrapper to support libSoup3 and libSoup2.4 simultaneously in the extension | ||
runtime and in the preferences window. | ||
*/ | ||
const SoupBowl = Self.imports.soupBowl; | ||
|
||
var BaseAdapter = class { | ||
_wallpaperLocation = null; | ||
|
||
constructor(wallpaperLocation) { | ||
this.logger = new LoggerModule.Logger('RWG3', 'BaseAdapter'); | ||
|
||
this._wallpaperLocation = wallpaperLocation; | ||
} | ||
|
||
/** | ||
* Retrieves a new url for an image and calls the given callback with an HistoryEntry as parameter. | ||
* The history element will be null and the error will be set if an error occurred. | ||
* | ||
* @param callback(historyElement, error) | ||
*/ | ||
requestRandomImage(callback) { | ||
this._error("requestRandomImage not implemented", callback); | ||
} | ||
|
||
fileName(uri) { | ||
while (this._isURIEncoded(uri)) { | ||
uri = decodeURIComponent(uri); | ||
} | ||
|
||
let base = uri.substring(uri.lastIndexOf('/') + 1); | ||
if (base.indexOf('?') >= 0) { | ||
base = base.substr(0, base.indexOf('?')); | ||
} | ||
return base; | ||
} | ||
|
||
/** | ||
* copy file from uri to local wallpaper directory and calls the given callback with the name and the full filepath | ||
* of the written file as parameter. | ||
* @param uri | ||
* @param callback(name, path, error) | ||
*/ | ||
fetchFile(uri, callback) { | ||
//extract the name from the url and | ||
let date = new Date(); | ||
let name = date.getTime() + '_' + this.fileName(uri); // timestamp ensures uniqueness | ||
|
||
let bowl = new SoupBowl.Bowl(); | ||
|
||
let file = Gio.file_new_for_path(this._wallpaperLocation + String(name)); | ||
let fstream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); | ||
|
||
// start the download | ||
let request = bowl.Soup.Message.new('GET', uri); | ||
|
||
bowl.send_and_receive(request, (response_data_bytes) => { | ||
if (!response_data_bytes) { | ||
fstream.close(null); | ||
|
||
if (callback) { | ||
callback(null, null, 'Not a valid response'); | ||
} | ||
|
||
return; | ||
} | ||
|
||
try { | ||
fstream.write(response_data_bytes, null); | ||
|
||
fstream.close(null); | ||
|
||
// call callback with the name and the full filepath of the written file as parameter | ||
if (callback) { | ||
callback(name, file.get_path()); | ||
} | ||
} catch (e) { | ||
if (callback) { | ||
callback(null, null, e); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
_isURIEncoded(uri) { | ||
uri = uri || ''; | ||
|
||
try { | ||
return uri !== decodeURIComponent(uri); | ||
} catch (err) { | ||
this.logger.error(err); | ||
return false; | ||
} | ||
} | ||
|
||
_error(err, callback) { | ||
let error = { "error": err }; | ||
this.logger.error(JSON.stringify(error)); | ||
|
||
if (callback) { | ||
callback(null, error); | ||
} | ||
} | ||
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
const ByteArray = imports.byteArray; | ||
|
||
const Self = imports.misc.extensionUtils.getCurrentExtension(); | ||
const HistoryModule = Self.imports.history; | ||
const JSONPath = Self.imports.jsonpath.jsonpath; | ||
const SettingsModule = Self.imports.settings; | ||
const SoupBowl = Self.imports.soupBowl; | ||
|
||
const BaseAdapter = Self.imports.adapter.baseAdapter; | ||
|
||
const RWG_SETTINGS_SCHEMA_GENERIC_JSON = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.genericJSON'; | ||
|
||
var GenericJsonAdapter = class extends BaseAdapter.BaseAdapter { | ||
constructor(id, wallpaperLocation) { | ||
super(wallpaperLocation); | ||
this._jsonPathParser = new JSONPath.JSONPathParser(); | ||
let path = `/org/gnome/shell/extensions/space-iflow-randomwallpaper/sources/genericJSON/${id}/`; | ||
this._settings = new SettingsModule.Settings(RWG_SETTINGS_SCHEMA_GENERIC_JSON, path); | ||
this.bowl = new SoupBowl.Bowl(); | ||
} | ||
|
||
requestRandomImage(callback) { | ||
let url = this._settings.get("request-url", "string"); | ||
url = encodeURI(url); | ||
let message = this.bowl.Soup.Message.new('GET', url); | ||
if (message === null) { | ||
this._error("Could not create request.", callback); | ||
return; | ||
} | ||
|
||
this.bowl.send_and_receive(message, (response_body_bytes) => { | ||
try { | ||
const response_body = JSON.parse(ByteArray.toString(response_body_bytes)); | ||
|
||
let imageJSONPath = this._settings.get("image-path", "string"); | ||
let postJSONPath = this._settings.get("post-path", "string"); | ||
let domainUrl = this._settings.get("domain", "string"); | ||
let authorNameJSONPath = this._settings.get("author-name-path", "string"); | ||
let authorUrlJSONPath = this._settings.get("author-url-path", "string"); | ||
|
||
let identifier = this._settings.get("name", "string"); | ||
if (identifier === null || identifier === "") { | ||
identifier = 'Generic JSON Source'; | ||
} | ||
|
||
let rObject = this._jsonPathParser.access(response_body, imageJSONPath); | ||
let imageDownloadUrl = this._settings.get("image-prefix", "string") + rObject.Object; | ||
|
||
// '@random' would yield different results so lets make sure the values stay | ||
// the same as long as the path is identical | ||
let samePath = imageJSONPath.substring(0, this.findFirstDifference(imageJSONPath, postJSONPath)); | ||
|
||
// count occurrences of '@random' to slice the array later | ||
// https://stackoverflow.com/a/4009768 | ||
let occurrences = (samePath.match(/@random/g) || []).length; | ||
let slicedRandomElements = rObject.RandomElements.slice(0, occurrences); | ||
|
||
let postUrl = this._jsonPathParser.access(response_body, postJSONPath, slicedRandomElements, false).Object; | ||
postUrl = this._settings.get("post-prefix", "string") + postUrl; | ||
if (typeof postUrl !== 'string' || !postUrl instanceof String) { | ||
postUrl = null; | ||
} | ||
|
||
let authorName = this._jsonPathParser.access(response_body, authorNameJSONPath, slicedRandomElements, false).Object; | ||
if (typeof authorName !== 'string' || !authorName instanceof String) { | ||
authorName = null; | ||
} | ||
|
||
let authorUrl = this._jsonPathParser.access(response_body, authorUrlJSONPath, slicedRandomElements, false).Object; | ||
authorUrl = this._settings.get("author-url-prefix", "string") + authorUrl; | ||
if (typeof authorUrl !== 'string' || !authorUrl instanceof String) { | ||
authorUrl = null; | ||
} | ||
|
||
if (callback) { | ||
let historyEntry = new HistoryModule.HistoryEntry(authorName, identifier, imageDownloadUrl); | ||
|
||
if (authorUrl !== null && authorUrl !== "") { | ||
historyEntry.source.authorUrl = authorUrl; | ||
} | ||
|
||
if (postUrl !== null && postUrl !== "") { | ||
historyEntry.source.imageLinkUrl = postUrl; | ||
} | ||
|
||
if (domainUrl !== null && domainUrl !== "") { | ||
historyEntry.source.sourceUrl = domainUrl; | ||
} | ||
|
||
callback(historyEntry); | ||
} | ||
} catch (e) { | ||
this._error("Unexpected response. (" + e + ")", callback); | ||
} | ||
}); | ||
} | ||
|
||
// https://stackoverflow.com/a/32859917 | ||
findFirstDifference(jsonPath1, jsonPath2) { | ||
let i = 0; | ||
if (jsonPath1 === jsonPath2) return -1; | ||
while (jsonPath1[i] === jsonPath2[i]) i++; | ||
return i; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
const Gio = imports.gi.Gio; | ||
|
||
const Self = imports.misc.extensionUtils.getCurrentExtension(); | ||
const SettingsModule = Self.imports.settings; | ||
const HistoryModule = Self.imports.history; | ||
|
||
const BaseAdapter = Self.imports.adapter.baseAdapter; | ||
|
||
const RWG_SETTINGS_SCHEMA_LOCAL_FOLDER = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.localFolder'; | ||
|
||
var LocalFolderAdapter = class extends BaseAdapter.BaseAdapter { | ||
constructor(id, wallpaperLocation) { | ||
super(wallpaperLocation); | ||
|
||
let path = `/org/gnome/shell/extensions/space-iflow-randomwallpaper/sources/localFolder/${id}/`; | ||
this._settings = new SettingsModule.Settings(RWG_SETTINGS_SCHEMA_LOCAL_FOLDER, path); | ||
} | ||
|
||
requestRandomImage(callback) { | ||
const folder = Gio.file_new_for_path(this._settings.get('folder', 'string')); | ||
let files = this._listDirectory(folder); | ||
|
||
if (files === null || files.length < 1) { | ||
this._error("Empty array.", callback); | ||
} | ||
|
||
let randomFile = files[Math.floor(Math.random() * files.length)].get_path(); | ||
|
||
let identifier = this._settings.get("name", "string"); | ||
if (identifier === null || identifier === "") { | ||
identifier = 'Local Folder'; | ||
} | ||
|
||
if (callback) { | ||
let historyEntry = new HistoryModule.HistoryEntry(null, identifier, randomFile); | ||
historyEntry.source.sourceUrl = this._wallpaperLocation; | ||
callback(historyEntry); | ||
} | ||
} | ||
|
||
fetchFile(path, callback) { | ||
let date = new Date(); | ||
let sourceFile = Gio.file_new_for_path(path); | ||
let name = `${date.getTime()}_${sourceFile.get_basename()}` | ||
let targetFile = Gio.file_new_for_path(this._wallpaperLocation + String(name)); | ||
|
||
// https://gjs.guide/guides/gio/file-operations.html#copying-and-moving-files | ||
sourceFile.copy(targetFile, Gio.FileCopyFlags.NONE, null, null); | ||
|
||
if (callback) { | ||
callback(name, targetFile.get_path()); | ||
} | ||
} | ||
|
||
// https://gjs.guide/guides/gio/file-operations.html#recursively-deleting-a-directory | ||
_listDirectory(directory) { | ||
const iterator = directory.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); | ||
|
||
let files = []; | ||
while (true) { | ||
const info = iterator.next_file(null); | ||
|
||
if (info === null) { | ||
break; | ||
} | ||
|
||
const child = iterator.get_child(info); | ||
const type = info.get_file_type(); | ||
|
||
switch (type) { | ||
case Gio.FileType.DIRECTORY: | ||
files = files.concat(this._listDirectory(child)); | ||
break; | ||
|
||
default: | ||
break; | ||
} | ||
|
||
let contentType = info.get_content_type(); | ||
if (contentType === 'image/png' || contentType === 'image/jpeg') { | ||
files.push(child); | ||
} | ||
} | ||
|
||
return files; | ||
} | ||
}; |
Oops, something went wrong.