mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-23 12:09:30 -07:00
parse rss feed #57
This commit is contained in:
91
server/scripts/modules/custom-rss-feed.mjs
Normal file
91
server/scripts/modules/custom-rss-feed.mjs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import Setting from './utils/setting.mjs';
|
||||||
|
import { reset as resetScroll } from './currentweatherscroll.mjs';
|
||||||
|
import { json } from './utils/fetch.mjs';
|
||||||
|
|
||||||
|
let firstRun = true;
|
||||||
|
|
||||||
|
const parser = new DOMParser();
|
||||||
|
|
||||||
|
// change of enable handler
|
||||||
|
const changeEnable = (newValue) => {
|
||||||
|
let newDisplay;
|
||||||
|
if (newValue) {
|
||||||
|
// add the feed to the scroll
|
||||||
|
getFeed(customFeed.value);
|
||||||
|
// show the string box
|
||||||
|
newDisplay = 'block';
|
||||||
|
} else {
|
||||||
|
// set scroll back to original
|
||||||
|
resetScroll();
|
||||||
|
// hide the string entry
|
||||||
|
newDisplay = 'none';
|
||||||
|
}
|
||||||
|
const stringEntry = document.getElementById('settings-customFeed-label');
|
||||||
|
if (stringEntry) {
|
||||||
|
stringEntry.style.display = newDisplay;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// get the rss feed and then swap out the current weather scroll
|
||||||
|
const getFeed = async (url) => {
|
||||||
|
// skip getting the feed on first run
|
||||||
|
if (firstRun) return;
|
||||||
|
|
||||||
|
// test validity
|
||||||
|
if (url === undefined || url === '') return;
|
||||||
|
|
||||||
|
// get the text as a string
|
||||||
|
// it needs to be proxied, use a free service
|
||||||
|
const rssResponse = await json(`https://api.allorigins.win/get?url=${url}`);
|
||||||
|
|
||||||
|
// this returns a data url
|
||||||
|
// a few sanity checks
|
||||||
|
if (rssResponse.status.content_type.indexOf('application/rss+xml') < 0) return;
|
||||||
|
if (rssResponse.contents.indexOf('base64') > 100) return;
|
||||||
|
// base 64 decode everything after the comma
|
||||||
|
const rss = atob(rssResponse.contents.split(',')[1]);
|
||||||
|
|
||||||
|
// parse the rss
|
||||||
|
const doc = parser.parseFromString(rss, 'text/xml');
|
||||||
|
|
||||||
|
// get the title
|
||||||
|
const title = doc.querySelector('channel title').innerHTML;
|
||||||
|
|
||||||
|
// get each item
|
||||||
|
const titles = [...doc.querySelectorAll('item title')].map((t) => t.innerHTML);
|
||||||
|
};
|
||||||
|
|
||||||
|
// change the feed source and re-load if necessary
|
||||||
|
const changeFeed = (newValue) => {
|
||||||
|
// first pass through won't have custom feed enable ready
|
||||||
|
if (firstRun) return;
|
||||||
|
|
||||||
|
if (customFeedEnable.value) {
|
||||||
|
getFeed(newValue);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const customFeed = new Setting('customFeed', {
|
||||||
|
name: 'Custom RSS Feed',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'string',
|
||||||
|
changeAction: changeFeed,
|
||||||
|
});
|
||||||
|
|
||||||
|
const customFeedEnable = new Setting('customFeedEnable', {
|
||||||
|
name: 'Enable Custom RSS Feed',
|
||||||
|
defaultValue: false,
|
||||||
|
changeAction: changeEnable,
|
||||||
|
});
|
||||||
|
|
||||||
|
// initialize the custom feed inputs on the page
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// add the controls to the page
|
||||||
|
const settingsSection = document.querySelector('#settings');
|
||||||
|
settingsSection.append(customFeedEnable.generate(), customFeed.generate());
|
||||||
|
// clear the first run value
|
||||||
|
firstRun = false;
|
||||||
|
// call change enable with the current value to show/hide the url box
|
||||||
|
// and make the call to get the feed if enabled
|
||||||
|
changeEnable(customFeedEnable.value);
|
||||||
|
});
|
||||||
@@ -20,26 +20,25 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const scanMusicDirectory = async () => {
|
const scanMusicDirectory = async () => {
|
||||||
const parseDirectory = async (path, prefix = "") => {
|
const parseDirectory = async (path, prefix = '') => {
|
||||||
const listing = await text(path);
|
const listing = await text(path);
|
||||||
const matches = [...listing.matchAll(/href="([^\"]+\.mp3)"/gi)];
|
const matches = [...listing.matchAll(/href="([^"]+\.mp3)"/gi)];
|
||||||
return matches.map((m) => `${prefix}${m[1]}`);
|
return matches.map((m) => `${prefix}${m[1]}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let files = await parseDirectory("music/");
|
let files = await parseDirectory('music/');
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
files = await parseDirectory("music/default/", "default/");
|
files = await parseDirectory('music/default/', 'default/');
|
||||||
}
|
}
|
||||||
return { availableFiles: files };
|
return { availableFiles: files };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Unable to scan music directory");
|
console.error('Unable to scan music directory');
|
||||||
console.error(e);
|
console.error(e);
|
||||||
return { availableFiles: [] };
|
return { availableFiles: [] };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getMedia = async () => {
|
const getMedia = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('playlist.json');
|
const response = await fetch('playlist.json');
|
||||||
@@ -221,7 +220,7 @@ const playerEnded = () => {
|
|||||||
const setTrackName = (fileName) => {
|
const setTrackName = (fileName) => {
|
||||||
const baseName = fileName.split('/').pop();
|
const baseName = fileName.split('/').pop();
|
||||||
const trackName = decodeURIComponent(
|
const trackName = decodeURIComponent(
|
||||||
baseName.replace(/\.mp3/gi, '').replace(/(_-)/gi, '')
|
baseName.replace(/\.mp3/gi, '').replace(/(_-)/gi, ''),
|
||||||
);
|
);
|
||||||
document.getElementById('musicTrack').innerHTML = trackName;
|
document.getElementById('musicTrack').innerHTML = trackName;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ class Setting {
|
|||||||
// couldn't parse as a float, store as a string
|
// couldn't parse as a float, store as a string
|
||||||
urlState = urlValue;
|
urlState = urlValue;
|
||||||
}
|
}
|
||||||
|
if (this.type === 'string' && urlValue !== undefined) {
|
||||||
|
urlState = urlValue;
|
||||||
|
}
|
||||||
|
|
||||||
// get existing value if present
|
// get existing value if present
|
||||||
const storedValue = urlState ?? this.getFromLocalStorage();
|
const storedValue = urlState ?? this.getFromLocalStorage();
|
||||||
@@ -60,6 +63,9 @@ class Setting {
|
|||||||
case 'select':
|
case 'select':
|
||||||
this.selectChange({ target: { value: this.myValue } });
|
this.selectChange({ target: { value: this.myValue } });
|
||||||
break;
|
break;
|
||||||
|
case 'string':
|
||||||
|
this.stringChange({ target: { value: this.myValue } });
|
||||||
|
break;
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
default:
|
default:
|
||||||
this.checkboxChange({ target: { checked: this.myValue } });
|
this.checkboxChange({ target: { checked: this.myValue } });
|
||||||
@@ -124,6 +130,33 @@ class Setting {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateString() {
|
||||||
|
// create a string input and accompanying set button
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.for = `settings-${this.shortName}-string`;
|
||||||
|
label.id = `settings-${this.shortName}-label`;
|
||||||
|
// text input box
|
||||||
|
const textInput = document.createElement('input');
|
||||||
|
textInput.type = 'text';
|
||||||
|
textInput.value = this.myValue;
|
||||||
|
textInput.id = `settings-${this.shortName}-string`;
|
||||||
|
textInput.name = `settings-${this.shortName}-string`;
|
||||||
|
// set button
|
||||||
|
const setButton = document.createElement('input');
|
||||||
|
setButton.type = 'button';
|
||||||
|
setButton.value = 'Set';
|
||||||
|
setButton.id = `settings-${this.shortName}-button`;
|
||||||
|
setButton.name = `settings-${this.shortName}-button`;
|
||||||
|
setButton.addEventListener('click', () => {
|
||||||
|
this.stringChange({ target: { value: textInput.value } });
|
||||||
|
});
|
||||||
|
// assemble
|
||||||
|
label.append(textInput, setButton);
|
||||||
|
|
||||||
|
this.element = label;
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
checkboxChange(e) {
|
checkboxChange(e) {
|
||||||
// update the state
|
// update the state
|
||||||
this.myValue = e.target.checked;
|
this.myValue = e.target.checked;
|
||||||
@@ -146,6 +179,15 @@ class Setting {
|
|||||||
this.changeAction(this.myValue);
|
this.changeAction(this.myValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stringChange(e) {
|
||||||
|
// update the value
|
||||||
|
this.myValue = e.target.value;
|
||||||
|
this.storeToLocalStorage(this.myValue);
|
||||||
|
|
||||||
|
// call the change action
|
||||||
|
this.changeAction(this.myValue);
|
||||||
|
}
|
||||||
|
|
||||||
storeToLocalStorage(value) {
|
storeToLocalStorage(value) {
|
||||||
if (!this.sticky) return;
|
if (!this.sticky) return;
|
||||||
const allSettingsString = localStorage?.getItem(SETTINGS_KEY) ?? '{}';
|
const allSettingsString = localStorage?.getItem(SETTINGS_KEY) ?? '{}';
|
||||||
@@ -163,8 +205,8 @@ class Setting {
|
|||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
return storedValue;
|
|
||||||
case 'select':
|
case 'select':
|
||||||
|
case 'string':
|
||||||
return storedValue;
|
return storedValue;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
@@ -214,6 +256,8 @@ class Setting {
|
|||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'select':
|
case 'select':
|
||||||
return this.generateSelect();
|
return this.generateSelect();
|
||||||
|
case 'string':
|
||||||
|
return this.generateString();
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
default:
|
default:
|
||||||
return this.generateCheckbox();
|
return this.generateCheckbox();
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
<script type="module" src="scripts/modules/radar.mjs"></script>
|
<script type="module" src="scripts/modules/radar.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/settings.mjs"></script>
|
<script type="module" src="scripts/modules/settings.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/media.mjs"></script>
|
<script type="module" src="scripts/modules/media.mjs"></script>
|
||||||
|
<script type="module" src="scripts/modules/custom-rss-feed.mjs"></script>
|
||||||
<script type="module" src="scripts/index.mjs"></script>
|
<script type="module" src="scripts/index.mjs"></script>
|
||||||
<!-- data -->
|
<!-- data -->
|
||||||
<script type="text/javascript" src="scripts/data/travelcities.js"></script>
|
<script type="text/javascript" src="scripts/data/travelcities.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user