Skip to content

Commit

Permalink
[radio@driglu4it] Fix soup3 issue and remove me as a maintainer (lin…
Browse files Browse the repository at this point in the history
…uxmint#5174)

* add preperation for soup3

* add soup3 support

* update version
  • Loading branch information
jonath92 authored Dec 4, 2023
1 parent be10ab7 commit 86a72d9
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 120 deletions.
5 changes: 4 additions & 1 deletion radio@driglu4it/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,7 @@ For the youtube download feature you can either use [youtube-dl](https://github.

- Special characters (e.g German Umlaute) are not shown correctly on the sound applet (when the option "Show song information on the panel" is activated). This is a [bug](https://github.com/mpv-player/mpv/issues/8844) of the mpv player. There is an open [pull request](https://github.com/mpv-player/mpv/pull/8845) which will fix that. At the moment the only workaround is to compile mpv from the source of the mentioned pull request.
- For some radio stations (e.g. BBC) the metadata can't be shown
- It is automatically downloaded the compiled version of the mpv mpris plugin which however is only suitable for 64-bit x86 architecutres (e.g. not for the raspberry pi). In that case you unfortunately have to [build the plugin](https://github.com/hoyon/mpv-mpris#build) by yourself at the moment and place the plugin at: `~/.cinnamon/configs/radio@driglu4it/.mpris.so`.
- It is automatically downloaded the compiled version of the mpv mpris plugin which however is only suitable for 64-bit x86 architecutres (e.g. not for the raspberry pi). In that case you unfortunately have to [build the plugin](https://github.com/hoyon/mpv-mpris#build) by yourself at the moment and place the plugin at: `~/.cinnamon/configs/radio@driglu4it/.mpris.so`.

## New Maintainer Needed
I (https://github.com/jonath92) have been expanding and maintaining the Radio Applet for a while now, but for personal reasons I unfortunately have very little time for this project. Therefore I am looking for a new mainainter. I am still more than willing to help a new maintainer with technical questions about the code. If you need help regarding the code, just open an issue in the applet repo and notify me in the issue.
2 changes: 1 addition & 1 deletion radio@driglu4it/files/radio@driglu4it/4.6/allStations.json

Large diffs are not rendered by default.

114 changes: 79 additions & 35 deletions radio@driglu4it/files/radio@driglu4it/4.6/radio-applet.js
Original file line number Diff line number Diff line change
Expand Up @@ -5569,8 +5569,9 @@ function createYouTubeDownloadIcon() {
}

;// CONCATENATED MODULE: ./src/lib/HttpHandler.ts
const { Message, SessionAsync } = imports.gi.Soup;
const httpSession = new SessionAsync();
const { Message, SessionAsync, Session } = imports.gi.Soup;
const { PRIORITY_DEFAULT } = imports.gi.GLib;
const ByteArray = imports.byteArray;
function isHttpError(x) {
return typeof x.reason_phrase === "string";
}
Expand All @@ -5596,30 +5597,70 @@ function checkForHttpError(message) {
}
: false;
}
function makeJsonHttpRequest(args) {
const { url, method = "GET", onErr, onSuccess, onSettled, headers, } = args;
const uri = url;
// const uri = queryParams ? `${url}?${stringify(queryParams)}` : url
const message = Message.new(method, uri);
if (!message) {
throw new Error(`Message Object couldn't be created`);
}
headers &&
Object.entries(headers).forEach(([key, value]) => {
message.request_headers.append(key, value);
});
httpSession.queue_message(message, (session, msgResponse) => {
onSettled === null || onSettled === void 0 ? void 0 : onSettled();
const error = checkForHttpError(msgResponse);
if (error) {
onErr(error);
return;
}
// TODO: We should actually check if this is really of type T1
const data = JSON.parse(msgResponse.response_body.data);
onSuccess(data);
});
}
const createSoup2HttpHandler = () => {
const httpSession = new SessionAsync();
return {
makeJsonHttpRequest: (args) => {
const { url, method = "GET", onErr, onSuccess, onSettled, headers, } = args;
// const uri = queryParams ? `${url}?${stringify(queryParams)}` : url
const message = Message.new(method, url);
if (!message) {
throw new Error(`Message Object couldn't be created`);
}
headers &&
Object.entries(headers).forEach(([key, value]) => {
message.request_headers.append(key, value);
});
httpSession.queue_message(message, (session, msgResponse) => {
onSettled === null || onSettled === void 0 ? void 0 : onSettled();
const error = checkForHttpError(msgResponse);
if (error) {
onErr(error);
return;
}
// TODO: We should actually check if this is really of type T1
const data = JSON.parse(msgResponse.response_body.data);
onSuccess(data);
});
},
};
};
const createSoup3HttpHandler = () => {
const httpSession = new Session();
return {
makeJsonHttpRequest: (args) => {
const { url, method = "GET", onErr, onSuccess, onSettled, headers, } = args;
const message = Message.new(method, url);
if (!message) {
throw new Error(`Message Object couldn't be created`);
}
headers &&
Object.entries(headers).forEach(([key, value]) => {
message.request_headers.append(key, value);
});
httpSession.send_and_read_async(message, PRIORITY_DEFAULT, null, (session, result) => {
const res = httpSession.send_and_read_finish(result);
// TODO: check for error
const responseBody = res != null ? ByteArray.toString(ByteArray.fromGBytes(res)) : null;
if (!responseBody) {
onErr({
code: 0,
reason_phrase: "no network response",
message: "no response body",
});
return;
}
if (responseBody) {
const data = JSON.parse(responseBody);
onSuccess(data);
}
});
},
};
};
const { makeJsonHttpRequest } = imports.gi.Soup.MAJOR_VERSION == 2
? createSoup2HttpHandler()
: createSoup3HttpHandler();

;// CONCATENATED MODULE: ./src/ui/RadioPopupMenu/UpdateStationsMenuItem.ts

Expand All @@ -5628,15 +5669,18 @@ function makeJsonHttpRequest(args) {
const { File: UpdateStationsMenuItem_File, FileCreateFlags } = imports.gi.Gio;
const { Bytes } = imports.gi.GLib;
const saveStations = (stationsUnfiltered) => {
const filteredStations = stationsUnfiltered.flatMap(({ name, url }, index) => {
const filteredStations = stationsUnfiltered
.flatMap(({ name, url }, index) => {
const isDuplicate = stationsUnfiltered.findIndex((val) => val.name === name && val.url === url) !== index;
if (isDuplicate)
return [];
if (name.length > 200 || url.length > 200) {
// some stations have unnormal long names/urls - probably due to some encoding issue on radio browser api side or so.
const trimmedName = name.trim();
const trimmedUrl = url.trim();
if (trimmedName.length === 0 || trimmedName.length > 200 || trimmedUrl.length > 200) {
// some stations have unnormal long names/urls - probably due to some encoding issue on radio browser api side or so.
return [];
}
return [[name.trim(), url.trim()]];
return [[trimmedName, trimmedUrl]];
})
// We need to sort our self - even though they should already be sorted - because some stations are wrongly shown first due to leading spaces
.sort((a, b) => a[0].localeCompare(b[0]));
Expand All @@ -5645,20 +5689,20 @@ const saveStations = (stationsUnfiltered) => {
file.create(FileCreateFlags.NONE, null);
}
file.replace_contents_bytes_async(new Bytes(JSON.stringify(filteredStations)), null, false, FileCreateFlags.REPLACE_DESTINATION, null, (file, result) => {
notify('Stations updated successfully');
notify("Stations updated successfully");
});
};
function createUpdateStationsMenuItem() {
const defaultText = 'Update Radio Stationlist';
const defaultText = "Update Radio Stationlist";
let isLoading = false;
const menuItem = createSimpleMenuItem({
text: defaultText,
onActivated: async (self) => {
if (isLoading)
return;
isLoading = true;
self.setText('Updating Radio stations...');
notify('Upating Radio stations... \n\nThis can take several minutes!');
self.setText("Updating Radio stations...");
notify("Upating Radio stations... \n\nThis can take several minutes!");
makeJsonHttpRequest({
url: "http://de1.api.radio-browser.info/json/stations",
onSuccess: (resp) => saveStations(resp),
Expand All @@ -5668,7 +5712,7 @@ function createUpdateStationsMenuItem() {
onSettled: () => {
self.setText(defaultText);
isLoading = false;
}
},
});
},
});
Expand Down
2 changes: 1 addition & 1 deletion radio@driglu4it/files/radio@driglu4it/metadata.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"uuid":"radio@driglu4it","name":"Radio++","description":"A radio applet with 1000's of searchable stations, mpris support, youtube-download function and more","max-instances":1,"multiversion":true,"version":"2.2.3"}
{"uuid":"radio@driglu4it","name":"Radio++","description":"A radio applet with 1000's of searchable stations, mpris support, youtube-download function and more","max-instances":1,"multiversion":true,"version":"2.2.4"}
5 changes: 3 additions & 2 deletions radio@driglu4it/info.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"author": "jonath92",
"author": "",
"original_author": "Driglu4it",
"contributors": [
"NikoKrause",
"claudiux"
"claudiux",
"jonath92"
]
}
2 changes: 1 addition & 1 deletion radio@driglu4it/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"author": "Jonathan Heard",
"license": "MIT",
"devDependencies": {
"@ci-types/cjs": "^5.4.2",
"@ci-types/cjs": "5.4.6",
"@types/jest": "^27.0.1",
"@types/lodash": "^4.14.170",
"@types/lodash-es": "^4.17.5",
Expand Down
162 changes: 115 additions & 47 deletions radio@driglu4it/src/lib/HttpHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { Message, SessionAsync } = imports.gi.Soup;

const httpSession = new SessionAsync();
const { Message, SessionAsync, Session } = imports.gi.Soup;
const { PRIORITY_DEFAULT } = imports.gi.GLib;
const ByteArray = imports.byteArray;

export interface HTTPParams {
[key: string]: boolean | string | number | undefined;
Expand Down Expand Up @@ -30,10 +30,9 @@ export interface LoadJsonArgs<T1> {
headers?: Headers;
onSuccess: (resp: T1) => void;
onErr: (err: HttpError) => void;
onSettled?: () => void
onSettled?: () => void;
}


function checkForHttpError(
message: imports.gi.Soup.Message
): HttpError | false {
Expand All @@ -52,48 +51,117 @@ function checkForHttpError(

return errMessage
? {
code,
reason_phrase,
message: errMessage,
}
code,
reason_phrase,
message: errMessage,
}
: false;
}

export function makeJsonHttpRequest<T1>(args: LoadJsonArgs<T1>) {
const {
url,
method = "GET",
onErr,
onSuccess,
onSettled,
headers,
} = args;

const uri = url;
// const uri = queryParams ? `${url}?${stringify(queryParams)}` : url
const message = Message.new(method, uri);

if (!message) {
throw new Error(`Message Object couldn't be created`);
}

headers &&
Object.entries(headers).forEach(([key, value]) => {
message.request_headers.append(key, value);
});


httpSession.queue_message(message, (session, msgResponse) => {
onSettled?.()
const error = checkForHttpError(msgResponse);

if (error) {
onErr(error);
return;
}

// TODO: We should actually check if this is really of type T1
const data = JSON.parse(msgResponse.response_body.data) as T1;
onSuccess(data);
});
}
type HttpHandler = {
makeJsonHttpRequest: <T1>(args: LoadJsonArgs<T1>) => void;
};

const createSoup2HttpHandler = (): HttpHandler => {
const httpSession = new SessionAsync();

return {
makeJsonHttpRequest: <T1>(args: LoadJsonArgs<T1>) => {
const {
url,
method = "GET",
onErr,
onSuccess,
onSettled,
headers,
} = args;

// const uri = queryParams ? `${url}?${stringify(queryParams)}` : url
const message = Message.new(method, url);

if (!message) {
throw new Error(`Message Object couldn't be created`);
}

headers &&
Object.entries(headers).forEach(([key, value]) => {
message.request_headers.append(key, value);
});

httpSession.queue_message(message, (session, msgResponse) => {
onSettled?.();
const error = checkForHttpError(msgResponse);

if (error) {
onErr(error);
return;
}

// TODO: We should actually check if this is really of type T1
const data = JSON.parse(msgResponse.response_body.data) as T1;
onSuccess(data);
});
},
};
};

const createSoup3HttpHandler = (): HttpHandler => {
const httpSession = new Session() as any;

return {
makeJsonHttpRequest: <T1>(args: LoadJsonArgs<T1>) => {
const {
url,
method = "GET",
onErr,
onSuccess,
onSettled,
headers,
} = args;

const message = Message.new(method, url);

if (!message) {
throw new Error(`Message Object couldn't be created`);
}

headers &&
Object.entries(headers).forEach(([key, value]) => {
message.request_headers.append(key, value);
});

httpSession.send_and_read_async(
message,
PRIORITY_DEFAULT,
null,
(session: any, result: any) => {
const res: imports.gi.GLib.Bytes | null =
httpSession.send_and_read_finish(result);

// TODO: check for error
const responseBody =
res != null ? ByteArray.toString(ByteArray.fromGBytes(res)) : null;

if (!responseBody) {
onErr({
code: 0,
reason_phrase: "no network response",
message: "no response body",
});
return;
}

if (responseBody) {
const data = JSON.parse(responseBody) as T1;
onSuccess(data);
}
}
);
},
};
};

export const { makeJsonHttpRequest } =
imports.gi.Soup.MAJOR_VERSION == 2
? createSoup2HttpHandler()
: createSoup3HttpHandler();
Loading

0 comments on commit 86a72d9

Please sign in to comment.