diff --git a/actions/FindfeedAction.php b/actions/FindfeedAction.php
new file mode 100644
index 00000000..25fe4714
--- /dev/null
+++ b/actions/FindfeedAction.php
@@ -0,0 +1,89 @@
+getBridgeClassNames() as $bridgeClassName) {
+ if (!$bridgeFactory->isEnabled($bridgeClassName)) {
+ continue;
+ }
+
+ $bridge = $bridgeFactory->create($bridgeClassName);
+
+ $bridgeParams = $bridge->detectParameters($targetURL);
+
+ if ($bridgeParams === null) {
+ continue;
+ }
+
+ // It's allowed to have no 'context' in a bridge (only a default context without any name)
+ // In this case, the reference to the parameters are found in the first element of the PARAMETERS array
+
+ $context = $bridgeParams['context'] ?? 0;
+
+ $bridgeData = [];
+ // Construct the array of parameters
+ foreach ($bridgeParams as $key => $value) {
+ // 'context' is a special case : it's a bridge parameters, there is no "name" for this parameter
+ if ($key == 'context') {
+ $bridgeData[$key]['name'] = 'Context';
+ $bridgeData[$key]['value'] = $value;
+ } else {
+ $bridgeData[$key]['name'] = $this->getParameterName($bridge, $context, $key);
+ $bridgeData[$key]['value'] = $value;
+ }
+ }
+
+ $bridgeParams['bridge'] = $bridgeClassName;
+ $bridgeParams['format'] = $format;
+ $content = [
+ 'url' => get_home_page_url() . '?action=display&' . http_build_query($bridgeParams),
+ 'bridgeParams' => $bridgeParams,
+ 'bridgeData' => $bridgeData,
+ 'bridgeMeta' => [
+ 'name' => $bridge::NAME,
+ 'description' => $bridge::DESCRIPTION,
+ 'parameters' => $bridge::PARAMETERS,
+ 'icon' => $bridge->getIcon(),
+ ],
+ ];
+ $results[] = $content;
+ }
+ if ($results === []) {
+ return new Response(Json::encode(['message' => 'No bridge found for given url']), 404, ['content-type' => 'application/json']);
+ }
+ return new Response(Json::encode($results), 200, ['content-type' => 'application/json']);
+ }
+
+ // Get parameter name in the actual context, or in the global parameter
+ private function getParameterName($bridge, $context, $key)
+ {
+ if (isset($bridge::PARAMETERS[$context][$key]['name'])) {
+ $name = $bridge::PARAMETERS[$context][$key]['name'];
+ } else if (isset($bridge::PARAMETERS['global'][$key]['name'])) {
+ $name = $bridge::PARAMETERS['global'][$key]['name'];
+ } else {
+ $name = 'Variable "' . $key . '" (No name provided)';
+ }
+ return $name;
+ }
+}
diff --git a/static/rss-bridge.js b/static/rss-bridge.js
index 498acd37..82069d8c 100644
--- a/static/rss-bridge.js
+++ b/static/rss-bridge.js
@@ -47,3 +47,82 @@ function rssbridge_toggle_bridge(){
bridge.getElementsByClassName('showmore-box')[0].checked = true;
}
}
+
+var rssbridge_feed_finder = (function() {
+ /*
+ * Code for "Find feed by URL" feature
+ */
+
+ // Start the Feed search
+ async function rssbridge_feed_search(event) {
+ const input = document.getElementById('searchfield');
+ let content = input.value;
+ if (content) {
+ const findfeedresults = document.getElementById('findfeedresults');
+ findfeedresults.innerHTML = 'Searching for matching feeds ...';
+ let baseurl = window.location.protocol + window.location.pathname;
+ let url = baseurl + '?action=findfeed&format=Html&url=' + content;
+ const response = await fetch(url);
+ if (response.ok) {
+ const data = await response.json();
+ rss_bridge_feed_display_found_feed(data);
+ } else {
+ rss_bridge_feed_display_feed_search_fail();
+ }
+ } else {
+ rss_bridge_feed_display_find_feed_empty();
+ }
+ }
+
+ // Display the found feeds
+ function rss_bridge_feed_display_found_feed(obj) {
+ const findfeedresults = document.getElementById('findfeedresults');
+
+ let content = 'Found Feed(s) :';
+
+ // Let's go throug every Feed found
+ for (const element of obj) {
+ content += `
+
+
+
+
+
+
+ ${element.bridgeMeta.description}
+
+
+
`;
+
+ // Now display every Feed parameter
+ for (const param in element.bridgeData) {
+ content += `${element.bridgeData[param].name} : ${element.bridgeData[param].value} `;
+ }
+ content += `
+
+
`;
+ }
+ content += '
This feed may be only one of the possible feeds. You may find more feeds using one of the bridges with different parameters, for example.
';
+ findfeedresults.innerHTML = content;
+ }
+
+ // Display an error if no feed were found
+ function rss_bridge_feed_display_feed_search_fail() {
+ const findfeedresults = document.getElementById('findfeedresults');
+ findfeedresults.innerHTML = 'No Feed found !Not every bridge supports feed detection. You can check below within the bridge parameters to create a feed.
';
+ }
+
+ // Empty the Found Feed section
+ function rss_bridge_feed_display_find_feed_empty() {
+ const findfeedresults = document.getElementById('findfeedresults');
+ findfeedresults.innerHTML = '';
+ }
+
+ // Add Event to 'Detect Feed" button
+ var rssbridge_feed_finder = function() {
+ const button = document.getElementById('findfeed');
+ button.addEventListener("click", rssbridge_feed_search);
+ button.addEventListener("keyup", rssbridge_feed_search);
+ };
+ return rssbridge_feed_finder;
+}());
diff --git a/static/style.css b/static/style.css
index a83e25e4..a9d5933a 100644
--- a/static/style.css
+++ b/static/style.css
@@ -453,3 +453,38 @@ button {
color: #d8d3cb;
}
}
+
+/* find-feed */
+.search-result {
+ background-color: #f0f0f0;
+ border-radius: 5px;
+ padding: 15px;
+ display: flex;
+ position: relative;
+ text-align: left;
+}
+@media (prefers-color-scheme: dark) {
+ .search-result {
+ background-color: #202325;
+ }
+}
+.search-result h2 {
+ color: #288cfc;
+}
+
+.search-result a {
+ text-decoration: none;
+ color: #248afa;
+}
+.search-result .icon {
+ margin: 0 15px 0 0;
+}
+.search-result span {
+ margin-right: 10px;
+}
+.search-result .description {
+ font-size: 110%;
+ margin-right: 0 !important;
+ margin-top: 5px !important;
+}
+/* end find-feed */
diff --git a/templates/frontpage.html.php b/templates/frontpage.html.php
index 63f4a2ab..99e2ffd9 100644
--- a/templates/frontpage.html.php
+++ b/templates/frontpage.html.php
@@ -2,6 +2,7 @@
@@ -15,6 +16,14 @@
onkeyup="rssbridge_list_search()"
value=""
>
+ Find Feed from URL
+
+
= raw($bridges) ?>