├── .gitignore ├── LICENSE.md ├── README.md ├── background.html ├── css └── popup.css ├── docs ├── get-rss-feed-url-extension_1.png ├── get-rss-feed-url-extension_2.png ├── get-rss-feed-url-extension_3.png ├── get-rss-feed-url-extension_github-repo.png ├── get-rss-feed-url-extension_medium-tag.png └── get-rss-feed-url-extension_reddit-sub.png ├── img ├── icon_128.png ├── icon_default-48.png ├── icon_default.png ├── icon_grey-38.png ├── icon_grey-48.png ├── icon_grey.png ├── notif_error.png ├── notif_info.png └── notif_success.png ├── js ├── background.js ├── functions.js ├── popup.js └── utilities.js ├── manifest.json └── popup.html /.gitignore: -------------------------------------------------------------------------------- 1 | js/config.js 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
4 | 5 | 6 | # What is Get RSS Feed URL Extension? 7 | 8 | 9 | **Get RSS Feed URL is a Google Chrome extension that provides links to the various RSS/Atom feeds of a website.** 10 | 11 | Indeed, websites do not always provide a direct link to the RSS feed. It is then necessary to look in the source code of the website and find the URL of the feed. 12 | 13 | This extension makes it possible to avoid this manipulation because the URLs of the RSS feeds of the website are displayed directly and can be copied with one click! 14 | 15 | In addition, this extension allows you to easily retrieve the RSS feed from several sites that do not offer one natively: 16 | 17 | * **Reddit:** the extension can retrieve RSS feeds from your Reddit homepage but also from a Reddit sub, a profile or post comments 18 | * **Kickstarter:** the RSS feed of a Kickstarter project is retrieved 19 | * **Vimeo:** RSS feed from a Vimeo channel 20 | * **Github:** the extension retrieves RSS feeds from a Github repository *(releases, commits, tags)* and from a Github user *(activity)* 21 | * **Gitlab:** same as Github 22 | * **Medium:** retrieves the RSS feed of a page with a keyword (tag) 23 | 24 |  25 | *Github repository* 26 | 27 |  28 | *Medium tag page* 29 | 30 |  31 | *Subreddit* 32 | 33 | 34 | # Install 35 | 36 | Install this extension from [**Chrome Web Store**](https://chrome.google.com/webstore/detail/get-rss-feed-url/kfghpdldaipanmkhfpdcjglncmilendn) or [**Microsoft Edge Store**](https://microsoftedge.microsoft.com/addons/detail/get-rss-feed-url/pgbelohmepchkohpdldadopkblkgbjom). 37 | 38 | 39 | # Usage 40 | 41 | After installing the extension, display it in your browser: 42 | 43 |  44 | 45 | Then go to a site and click on the extension button to display the different RSS feeds found: 46 | 47 |  48 | 49 | You can also open this popup from the keyboard shortcut: `Alt+Shift+R`. You can [edit it in your Chrome settings](chrome://extensions/shortcuts) (Extensions > Keyboard shortcuts). 50 | 51 | You can copy the URL of an RSS feed by clicking on the "Copy URL" button. 52 | If you want to copy the URLs of all RSS feeds found, click "Copy all URLs". 53 | 54 | 55 | # Feedback 56 | 57 | If you encounter a problem using Get RSS Feed URL extension, or would like to request an enhancement, feel free to create an issue or say hello on [Twitter/X](https://twitter.com/shevabam)! 58 | 59 | 60 | # Privacy Policy 61 | 62 | The extension does not collect any user information or other information. Some data can be requested using the XHR API (only GET requests) in order to provide more context to retrieve a website's RSS feed. 63 | 64 | The extension does not store any data on the browser or on the computer. 65 | 66 | 67 | 68 | --- 69 | 70 | 71 | 72 | # Qu'est-ce que Get RSS Feed URL Extension ? 73 | 74 | 75 | **Get RSS Feed URL est une extension Google Chrome qui permet d'obtenir les liens vers les différents flux RSS/Atom d'un site Internet.** 76 | 77 | En effet, les sites Internet ne mettent pas toujours à disposition un lien direct vers le flux RSS. Il faut alors chercher dans le code source et trouver l'URL du flux. 78 | 79 | Cette extension permet d'éviter cette manipulation car les URL des flux RSS du site Internet sont affichés directement et peuvent être copiés d'un simple clic ! 80 | 81 | De plus, cette extension vous permet de récupérer facilement le flux RSS de plusieurs sites qui n'en proposent pas nativement : 82 | 83 | * **Reddit :** l'extension arrive à récupérer les flux RSS de votre homepage Reddit mais aussi d'un sub Reddit, d'un profil ou des commentaires d'un post 84 | * **Kickstarter :** le flux RSS d'un projet Kickstarter est récupéré 85 | * **Vimeo :** flux RSS d'une chaîne Vimeo 86 | * **Github :** l'extension récupère les flux RSS d'un dépôt Github *(releases, commits, tags)* et d'un utilisateur Github *(activité)* 87 | * **Gitlab :** idem que Github 88 | * **Medium :** récupère le flux RSS d'une page d'un mot-clé (tag) 89 | 90 |  91 | *Github repository* 92 | 93 |  94 | *Medium tag page* 95 | 96 |  97 | *Subreddit* 98 | 99 | 100 | # Installation 101 | 102 | Installez l'extension à partir du [**Chrome Web Store**](https://chrome.google.com/webstore/detail/get-rss-feed-url/kfghpdldaipanmkhfpdcjglncmilendn?hl=fr) ou sur [**Microsoft Edge Store**](https://microsoftedge.microsoft.com/addons/detail/get-rss-feed-url/pgbelohmepchkohpdldadopkblkgbjom). 103 | 104 | 105 | # Utilisation 106 | 107 | Après avoir installé l'extension, affichez-la dans votre barre : 108 | 109 |  110 | 111 | Rendez-vous ensuite sur un site et cliquez sur le bouton de l'extension pour afficher les différents flux RSS trouvés : 112 | 113 |  114 | 115 | Vous pouvez aussi ouvrir cette popup à partir du raccourci clavier : `Alt+Shift+R`. Vous pouvez le [modifier dans vos paramètres Chrome](chrome://extensions/shortcuts) (Extensions > Raccourcis clavier). 116 | 117 | Vous pouvez copier l'URL d'un flux RSS en cliquant sur le bouton "Copy URL". 118 | Si vous souhaitez copier les URL de tous les flux RSS trouvés, cliquez sur "Copy all URLs". 119 | 120 | 121 | # Feedback 122 | 123 | Si vous rencontrez un problème avec l'extension Get RSS Feed URL, ou que vous souhaitez une évolution, n'hésitez pas à créer une *issue* ou à me contacter sur [Twitter/X](https://twitter.com/shevabam) ! 124 | 125 | 126 | # Politique de confidentialité 127 | 128 | L'extension ne collecte aucune information sur l'utilisateur ou d'autres informations. Certaines données peuvent être demandées à l'aide de l'API XHR (uniquement les requêtes GET) afin de fournir plus de contexte pour récupérer le flux RSS d'un site Internet. 129 | 130 | L'extension ne stocke aucune donnée sur le navigateur ou sur l'ordinateur. 131 | 132 | -------------------------------------------------------------------------------- /background.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /css/popup.css: -------------------------------------------------------------------------------- 1 | body.rss-feed-url { 2 | font-size: 14px; 3 | font-family: sans-serif; 4 | width: 300px; 5 | background-color: #fff; 6 | } 7 | 8 | .rss-feed-url_content { 9 | min-height: 50px; 10 | } 11 | .rss-feed-url #feeds { 12 | padding: 10px 0; 13 | max-height: 300px; 14 | overflow-x: hidden; 15 | overflow-y: auto; 16 | } 17 | .rss-feed-url #feeds table { 18 | width: 100%; 19 | border-collapse: collapse; 20 | } 21 | .rss-feed-url #feeds table tr:hover td { 22 | background-color: rgba(255, 156, 0, 0.1); 23 | } 24 | .rss-feed-url #feeds table td.feed-title { 25 | max-width: 100px; 26 | overflow: hidden; 27 | text-overflow: ellipsis; 28 | white-space: nowrap; 29 | padding: 8px 5px; 30 | } 31 | .rss-feed-url #feeds table td.feed-copy { 32 | width: 60px; 33 | text-align: right; 34 | } 35 | .rss-feed-url #feeds a.link { 36 | color: #000; 37 | text-decoration: underline; 38 | font-size: 14px; 39 | } 40 | .rss-feed-url #feeds a.link:hover { 41 | text-decoration: none; 42 | } 43 | .rss-feed-url #feeds span.feed-url { 44 | display: block; 45 | font-size: 10px; 46 | } 47 | .rss-feed-url #feeds a.copyButton { 48 | background-color: #ff9c00; 49 | color: #fff; 50 | font-size: 10px; 51 | padding: 5px; 52 | text-decoration: none; 53 | border-radius: 4px; 54 | display: inline-block; 55 | } 56 | .rss-feed-url #feeds a.copyButton:hover { 57 | background-color: #e48e04; 58 | } 59 | 60 | .rss-feed-url #feeds .copyAllLinks-container { 61 | text-align: right; 62 | margin-top: 5px; 63 | } 64 | .rss-feed-url #feeds a#copyAllLinks { 65 | color: #ff9c00; 66 | font-size: 10px; 67 | } 68 | .rss-feed-url #feeds a#copyAllLinks:hover { 69 | text-decoration: none; 70 | } 71 | 72 | .rss-feed-url header { 73 | background-color: #ff9c00; 74 | padding: 10px; 75 | border-radius: 5px; 76 | } 77 | .rss-feed-url header h1 { 78 | font-size: 18px; 79 | font-weight: normal; 80 | letter-spacing: 1px; 81 | text-align: center; 82 | color: #fff; 83 | font-family: sans-serif; 84 | } 85 | 86 | .rss-feed-url footer { 87 | background-color: #f0f0f0; 88 | text-align: center; 89 | font-size: .7rem; 90 | color: #888; 91 | padding: .6rem; 92 | border-radius: 5px; 93 | } 94 | .rss-feed-url footer a { 95 | color: #888; 96 | } 97 | .rss-feed-url footer a:hover { 98 | text-decoration: none; 99 | } 100 | span.rss-feed-url__bullet { 101 | padding: 0 1rem; 102 | } 103 | 104 | .rss-feed-url #rss-feed-url_response { 105 | display: none; 106 | } 107 | 108 | 109 | @keyframes loader-anim { 110 | 50% { 111 | box-shadow: #B1B1B1 65px 0, #B1B1B1 120px 0, #B1B1B1 -65px 0, #B1B1B1 -120px 0; 112 | transform: scale(.7) 113 | } 114 | } 115 | 116 | .loader { 117 | margin-top: 30px; 118 | width: 10px; 119 | height: 10px; 120 | background: #B1B1B1; 121 | border-radius: 50%; 122 | position: absolute; 123 | top: calc(50% - 20px); 124 | left: calc(50% - 20px); 125 | box-shadow: #B1B1B1 20px 0, #B1B1B1 40px 0, #B1B1B1 -20px 0, #B1B1B1 -40px 0; 126 | animation: loader-anim 1s infinite; 127 | } -------------------------------------------------------------------------------- /docs/get-rss-feed-url-extension_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/docs/get-rss-feed-url-extension_1.png -------------------------------------------------------------------------------- /docs/get-rss-feed-url-extension_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/docs/get-rss-feed-url-extension_2.png -------------------------------------------------------------------------------- /docs/get-rss-feed-url-extension_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/docs/get-rss-feed-url-extension_3.png -------------------------------------------------------------------------------- /docs/get-rss-feed-url-extension_github-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/docs/get-rss-feed-url-extension_github-repo.png -------------------------------------------------------------------------------- /docs/get-rss-feed-url-extension_medium-tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/docs/get-rss-feed-url-extension_medium-tag.png -------------------------------------------------------------------------------- /docs/get-rss-feed-url-extension_reddit-sub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/docs/get-rss-feed-url-extension_reddit-sub.png -------------------------------------------------------------------------------- /img/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/icon_128.png -------------------------------------------------------------------------------- /img/icon_default-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/icon_default-48.png -------------------------------------------------------------------------------- /img/icon_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/icon_default.png -------------------------------------------------------------------------------- /img/icon_grey-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/icon_grey-38.png -------------------------------------------------------------------------------- /img/icon_grey-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/icon_grey-48.png -------------------------------------------------------------------------------- /img/icon_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/icon_grey.png -------------------------------------------------------------------------------- /img/notif_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/notif_error.png -------------------------------------------------------------------------------- /img/notif_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/notif_info.png -------------------------------------------------------------------------------- /img/notif_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shevabam/get-rss-feed-url-extension/e38ec4aa3663b2f0d30c24ee30b47df0e7b6bdf0/img/notif_success.png -------------------------------------------------------------------------------- /js/background.js: -------------------------------------------------------------------------------- 1 | 2 | importScripts('utilities.js'); 3 | importScripts('functions.js'); 4 | 5 | chrome.tabs.onActivated.addListener(function(activeInfo) { 6 | // updateIcon(activeInfo.tabId); 7 | }); 8 | 9 | //listen for current tab to be changed 10 | chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { 11 | // updateIcon(tabId); 12 | }); 13 | 14 | function updateIcon(tabId) { 15 | chrome.tabs.get(tabId, function(change){ 16 | 17 | chrome.tabs.get(tabId, function(tab){ 18 | var url = tab.url; 19 | 20 | getFeedsURLs(url, function(feeds){ 21 | 22 | nbFeeds = feeds.length; 23 | 24 | // console.log('nbFeeds (bg) : '+nbFeeds); 25 | 26 | if (nbFeeds == 0) { 27 | chrome.action.setIcon({path: {"48": "/img/icon_grey-48.png"}, tabId: tabId}); 28 | chrome.action.setBadgeText({text: "", tabId: tabId}); 29 | } 30 | else { 31 | chrome.action.setIcon({path: {"48": "/img/icon_default-48.png"}, tabId: tabId}); 32 | chrome.action.setBadgeText({text: nbFeeds.toString(), tabId: tabId}); 33 | } 34 | 35 | }); 36 | }); 37 | }); 38 | }; -------------------------------------------------------------------------------- /js/functions.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Get HTML source code froms URL 4 | */ 5 | function getHtmlSource(url, callback) { 6 | fetch(url, { 7 | method: 'get' 8 | }) 9 | .then(function(response){ 10 | if (response.status == 200) { 11 | response.text().then(function(data){ 12 | callback(data); 13 | }) 14 | } 15 | else { 16 | render('Unable to find feed'); 17 | } 18 | }) 19 | .catch(function(error){ 20 | render('Error: '+error.message); 21 | }); 22 | } 23 | 24 | /** 25 | * Extacts link tag with some filters 26 | */ 27 | function extractLinkTags(html) { 28 | // let regex = /]*\btype=['"][^'"]+['"][^>]*>/gi; 29 | 30 | // Excludes link tags with: 31 | // rel="stylesheet" 32 | // rel="icon" 33 | // rel="search" 34 | // type="text/javascript" 35 | // type="image/" 36 | // type="font/" 37 | let regex = /]*\b(?:rel=['"](stylesheet|icon|search)['"]|type=['"](text\/javascript|image\/(.*)|font\/(.*))['"]))[^>]*\btype=['"][^'"]+['"][^>]*>/gi; 38 | 39 | let match = html.match(regex); 40 | 41 | return match || []; 42 | } 43 | 44 | 45 | const SERVICES_TO_CHECK = [ 46 | // 'Youtube', 47 | 'RedditRoot', 48 | 'RedditSub', 49 | 'RedditUser', 50 | 'RedditPostComments', 51 | 'Kickstarter', 52 | 'Vimeo', 53 | 'GithubRepo', 54 | 'GithubUser', 55 | 'GitlabRepo', 56 | 'GitlabUser', 57 | 'MediumTag' 58 | ]; 59 | 60 | function checkIfUrlIsKnown(url) { 61 | let match = false; 62 | let check = {}; 63 | 64 | for (const service of SERVICES_TO_CHECK) { 65 | const method = 'get' + service + 'Rss'; 66 | check = this[method](url); 67 | 68 | match = check.match; 69 | 70 | if (match === true) { 71 | break; 72 | } 73 | } 74 | 75 | if (match === true) { 76 | return check.feeds; 77 | } else { 78 | return false; 79 | } 80 | } 81 | 82 | 83 | /** 84 | * Get RSS feeds URLs 85 | */ 86 | function getFeedsURLs(url, callback) { 87 | 88 | switch (parseUrl(url).protocol) { 89 | case "chrome:": 90 | case "chrome-extension:": 91 | case "about:": 92 | case "vivaldi:": 93 | case "edge:": 94 | case "chrome-devtools:": 95 | case "devtools:": 96 | render('Unable to find feed'); 97 | return false; 98 | } 99 | 100 | var getFeedUrl = checkIfUrlIsKnown(url); 101 | 102 | if (false !== getFeedUrl && getFeedUrl.length > 0) { 103 | callback(getFeedUrl); 104 | } else { 105 | getHtmlSource(url, (response) => { 106 | if (response != '') { 107 | let linkTags = extractLinkTags(response); 108 | // console.log(linkTags); 109 | 110 | document.getElementById('rss-feed-url_response').innerHTML = linkTags; 111 | } 112 | 113 | searchFeed(url, callback); 114 | }); 115 | } 116 | } 117 | 118 | /** 119 | * Search RSS Feed in source code 120 | */ 121 | async function searchFeed(url, callback) { 122 | var feeds_urls = []; 123 | 124 | if (document.getElementById('rss-feed-url_response').innerHTML != '') { 125 | const types = [ 126 | 'application/rss+xml', 127 | 'application/atom+xml', 128 | 'application/rdf+xml', 129 | 'application/rss', 130 | 'application/atom', 131 | 'application/rdf', 132 | 'text/rss+xml', 133 | 'text/atom+xml', 134 | 'text/rdf+xml', 135 | 'text/rss', 136 | 'text/atom', 137 | 'text/rdf' 138 | ]; 139 | 140 | var links = document.getElementById('rss-feed-url_response').querySelectorAll("#rss-feed-url_response link[type]"); 141 | 142 | document.getElementById('rss-feed-url_response').innerHTML = ''; 143 | 144 | for (var i = 0; i < links.length; i++) { 145 | 146 | if (links[i].hasAttribute('type') && types.indexOf(links[i].getAttribute('type')) !== -1) { 147 | 148 | var feed_url = links[i].getAttribute('href'); 149 | 150 | // If feed's url starts with "//" 151 | if (feed_url.startsWith('//')) { 152 | feed_url = "http:" + feed_url; 153 | } 154 | // If feed's url starts with "/" 155 | else if (feed_url.startsWith('/')) { 156 | feed_url = url.split('/')[0] + '//' + url.split('/')[2] + feed_url; 157 | } 158 | // If feed's url starts with http or https 159 | else if (/^(http|https):\/\//i.test(feed_url)) { 160 | feed_url = feed_url; 161 | } 162 | // If feed's has no slash 163 | else if (!feed_url.match(/\//)) { 164 | feed_url = url.substr(0, url.lastIndexOf("/")) + '/' + feed_url; 165 | } 166 | else { 167 | feed_url = url + "/" + feed_url.replace(/^\//g, ''); 168 | } 169 | 170 | var feed = { 171 | type: links[i].getAttribute('type'), 172 | url: feed_url, 173 | title: links[i].getAttribute('title') || feed_url 174 | }; 175 | 176 | feeds_urls.push(feed); 177 | } 178 | } 179 | 180 | } 181 | 182 | if (feeds_urls.length === 0) { 183 | 184 | var test_feed = await tryToGetFeedURL(url); 185 | 186 | if (test_feed !== null) { 187 | feeds_urls.push(test_feed); 188 | } 189 | } 190 | 191 | callback(feeds_urls); 192 | 193 | if (feeds_urls.length === 0) { 194 | render('Unable to find feed'); 195 | } 196 | } 197 | 198 | 199 | 200 | /** 201 | * Get RSS feed URL of Youtube channel or user 202 | */ 203 | function getYoutubeRss(url) { 204 | let datas = { match: false, feeds: [] }; 205 | 206 | let regex = /^(http(s)?:\/\/)?((w){3}.)?youtu(be|.be)?(\.com)?\/(channel|user|c).+/i; 207 | let has_match = regex.test(url); 208 | 209 | if (has_match) { 210 | datas.match = true; 211 | let query = ''; 212 | let title = ''; 213 | 214 | let path = new URL(url).pathname; 215 | 216 | if (path.startsWith('/channel/')) { 217 | let channel_id = path.substr('/channel/'.length).split('/')[0]; 218 | query = 'channel_id=' + channel_id; 219 | title = channel_id; 220 | } else if (path.startsWith('/c/')) { 221 | let channel_id = path.substr('/c/'.length).split('/')[0]; 222 | query = 'user=' + channel_id; 223 | title = channel_id; 224 | } else if (path.startsWith('/user/')) { 225 | let user_id = path.substr('/user/'.length).split('/')[0]; 226 | query = 'user=' + user_id; 227 | title = user_id; 228 | } 229 | 230 | if (query != '') { 231 | datas.feeds.push({ 232 | url: 'https://www.youtube.com/feeds/videos.xml?' + query, 233 | title: title 234 | }); 235 | } 236 | } 237 | 238 | return datas; 239 | } 240 | 241 | 242 | /** 243 | * Get RSS feed URL for the Reddit homepage 244 | */ 245 | function getRedditRootRss(url) { 246 | let datas = { match: false, feeds: [] }; 247 | 248 | 249 | let regex = /^(http(s)?:\/\/)?((w){3}.)?reddit\.com(\/)?$/i; 250 | let has_match = regex.test(url); 251 | 252 | if (has_match) { 253 | datas.match = true; 254 | 255 | let feed_url = !url.endsWith('/') ? url+'/' : url; 256 | feed_url += '.rss'; 257 | 258 | if (feed_url) { 259 | datas.feeds.push({ 260 | url: feed_url, 261 | title: feed_url 262 | }); 263 | } 264 | } 265 | 266 | return datas; 267 | } 268 | 269 | /** 270 | * Get RSS feed URL of a subreddit 271 | */ 272 | function getRedditSubRss(url) { 273 | let datas = { match: false, feeds: [] }; 274 | 275 | let regex = /^(http(s)?:\/\/)?((w){3}.)?reddit\.com\/r\/(.+)/i; 276 | let has_match = regex.test(url); 277 | 278 | if (has_match) { 279 | datas.match = true; 280 | 281 | let feed_url = url.endsWith('/') ? url.slice(0, -1) : url; 282 | feed_url += '.rss'; 283 | 284 | if (feed_url) { 285 | datas.feeds.push({ 286 | url: feed_url, 287 | title: feed_url 288 | }); 289 | } 290 | } 291 | 292 | return datas; 293 | } 294 | 295 | /** 296 | * Get RSS feed URL of a reddit user 297 | */ 298 | function getRedditUserRss(url) { 299 | let datas = { match: false, feeds: [] }; 300 | 301 | let regex = /^(http(s)?:\/\/)?((w){3}.)?reddit\.com\/user\/(.+)/i; 302 | let has_match = regex.test(url); 303 | 304 | if (has_match) { 305 | datas.match = true; 306 | 307 | let feed_url = url.endsWith('/') ? url.slice(0, -1) : url; 308 | feed_url += '.rss'; 309 | 310 | if (feed_url) { 311 | datas.feeds.push({ 312 | url: feed_url, 313 | title: feed_url 314 | }); 315 | } 316 | } 317 | 318 | return datas; 319 | } 320 | 321 | /** 322 | * Get RSS feed URL for reddit post comments 323 | */ 324 | function getRedditPostCommentsRss(url) { 325 | let datas = { match: false, feeds: [] }; 326 | 327 | let regex = /^(http(s)?:\/\/)?((w){3}.)?reddit\.com\/r\/(.+)\/comments\/(.+)\/(.+)/i; 328 | let has_match = regex.test(url); 329 | 330 | if (has_match) { 331 | datas.match = true; 332 | 333 | let feed_url = url.endsWith('/') ? url.slice(0, -1) : url; 334 | feed_url += '.rss'; 335 | 336 | if (feed_url) { 337 | datas.feeds.push({ 338 | url: feed_url, 339 | title: feed_url 340 | }); 341 | } 342 | } 343 | 344 | return datas; 345 | } 346 | 347 | 348 | /** 349 | * Get RSS feed URL of kickstarter 350 | */ 351 | function getKickstarterRss(url) { 352 | let datas = { match: false, feeds: [] }; 353 | 354 | let regex = /^(http(s)?:\/\/)?((w){3}.)?kickstarter\.com/i; 355 | let has_match = regex.test(url); 356 | 357 | if (has_match) { 358 | datas.match = true; 359 | 360 | let feed_url = url.endsWith('/') ? url.slice(0, -1) : url; 361 | feed_url = feed_url.split('?')[0] + '/posts.atom'; 362 | 363 | if (feed_url) { 364 | datas.feeds.push({ 365 | url: feed_url, 366 | title: feed_url 367 | }); 368 | } 369 | } 370 | 371 | return datas; 372 | } 373 | 374 | /** 375 | * Get RSS feed URL of vimeo 376 | */ 377 | function getVimeoRss(url) { 378 | let datas = { match: false, feeds: [] }; 379 | 380 | let regex = /^(http(s)?:\/\/)?((w){3}.)?vimeo\.com\/([a-zA-Z](.+))(\/videos)?/i; 381 | let has_match = regex.test(url); 382 | 383 | if (has_match) { 384 | datas.match = true; 385 | 386 | let feed_url = url.endsWith('/videos') ? url.replace(/\/videos$/, '') + '/rss' : url + '/videos/rss'; 387 | 388 | if (feed_url) { 389 | datas.feeds.push({ 390 | url: feed_url, 391 | title: feed_url 392 | }); 393 | } 394 | } 395 | 396 | return datas; 397 | } 398 | 399 | /** 400 | * Get RSS feed URL of Github repo 401 | */ 402 | function getGithubRepoRss(url) { 403 | let datas = { match: false, feeds: [] }; 404 | 405 | let regex = /^(http(s)?:\/\/)?((w){3}.)?github\.com\/([a-zA-Z0-9](.+))\/([a-zA-Z0-9](.+))$/i; 406 | let matches = url.match(regex); 407 | 408 | if (matches) { 409 | datas.match = true; 410 | let repoUrl = matches[0].replace(/\/$/, ''); // Remove trailing slash 411 | repoUrl = repoUrl.replace(/\/(releases|commits|tags)$/, ''); 412 | 413 | datas.feeds.push({ url: repoUrl + '/releases.atom', title: 'Repo releases' }); 414 | datas.feeds.push({ url: repoUrl + '/commits.atom', title: 'Repo commits' }); 415 | datas.feeds.push({ url: repoUrl + '/tags.atom', title: 'Repo tags' }); 416 | } 417 | 418 | return datas; 419 | } 420 | 421 | /* 422 | * Get RSS feed URL of Github user 423 | */ 424 | function getGithubUserRss(url) { 425 | let datas = { match: false, feeds: [] }; 426 | 427 | let regex = /^(http(s)?:\/\/)?((w){3}.)?github\.com\/([a-zA-Z0-9](.+))$/i; 428 | let matches = url.match(regex); 429 | 430 | if (matches) { 431 | datas.match = true; 432 | let userUrl = matches[0].replace(/\/$/, ''); // Remove trailing slash 433 | datas.feeds.push({ url: userUrl + '.atom', title: 'User activity' }); 434 | } 435 | 436 | return datas; 437 | } 438 | 439 | /** 440 | * Get RSS feed URL of Gitlab repo 441 | */ 442 | function getGitlabRepoRss(url) { 443 | let datas = { match: false, feeds: [] }; 444 | 445 | let regex = /^(http(s)?:\/\/)?((w){3}.)?gitlab\.com\/([a-zA-Z0-9](.+))\/([a-zA-Z0-9](.+))$/i; 446 | let matches = url.match(regex); 447 | 448 | if (matches) { 449 | datas.match = true; 450 | let repoUrl = matches[0].replace(/\/$/, ''); // Remove trailing slash 451 | 452 | datas.feeds.push({ url: repoUrl + '.atom', title: 'Repo commits' }); 453 | } 454 | 455 | return datas; 456 | } 457 | 458 | /* 459 | * Get RSS feed URL of Gitlab user 460 | */ 461 | function getGitlabUserRss(url) { 462 | let datas = { match: false, feeds: [] }; 463 | 464 | let regex = /^(http(s)?:\/\/)?((w){3}.)?gitlab\.com\/([a-zA-Z0-9](.+))$/i; 465 | let matches = url.match(regex); 466 | 467 | if (matches) { 468 | datas.match = true; 469 | let userUrl = matches[0].replace(/\/$/, ''); // Remove trailing slash 470 | datas.feeds.push({ url: userUrl + '.atom', title: 'User activity' }); 471 | } 472 | 473 | return datas; 474 | } 475 | 476 | /** 477 | * Get RSS feed URL of a medium tag page 478 | */ 479 | function getMediumTagRss(url) { 480 | let datas = { match: false, feeds: [] }; 481 | 482 | let regex = /^(http(s)?:\/\/)?((w){3}.)?medium\.com\/tag\/(.+)/i; 483 | let has_match = regex.test(url); 484 | 485 | if (has_match) { 486 | datas.match = true; 487 | 488 | let tag = url.match(regex)[5]; 489 | 490 | let feed_url = url.replace(/(\/tag)/, '/feed$1'); 491 | 492 | if (feed_url) { 493 | datas.feeds.push({ 494 | url: feed_url, 495 | title: tag ?? feed_url 496 | }); 497 | } 498 | } 499 | 500 | return datas; 501 | } 502 | 503 | 504 | 505 | /** 506 | * Prints message in #feeds 507 | */ 508 | function render(content) { 509 | document.getElementById('feeds').innerHTML = content; 510 | } 511 | 512 | /** 513 | * Copy to clipboard text with notification 514 | */ 515 | function copyToClipboard(text, notification) { 516 | navigator.clipboard.writeText(text); 517 | 518 | chrome.notifications.create('get-rss-feed-url-copy', { 519 | type: "basic", 520 | title: notification.title || "Get RSS Feeds URLs", 521 | message: notification.message, 522 | iconUrl: "img/notif_"+notification.type+".png" 523 | }); 524 | } 525 | 526 | 527 | /** 528 | * Attempt to find an RSS feed URL by providing a suffix 529 | */ 530 | async function tryToGetFeedURL(tabUrl) { 531 | var url_datas = parseUrl(tabUrl); 532 | var feed = null; 533 | var isFound = false; 534 | 535 | var tests = ['/feed', '/rss', '/rss.xml', '/feed.xml', '/rss/news.xml', '/articles/feed', '/rss/index.html']; 536 | 537 | for (var t = 0; t < tests.length; t++) { 538 | if (isFound === false) { 539 | var feed_url = url_datas.origin + tests[t]; 540 | 541 | let response = await fetch(feed_url, { method: 'get' }); 542 | 543 | if (!response.ok || (response.status >= 200 && response.status < 400)) { 544 | let urlContent = await response.text(); 545 | 546 | var oParser = new DOMParser(); 547 | var oDOM = oParser.parseFromString(urlContent, "application/xml"); 548 | 549 | var getRssTag = oDOM.getElementsByTagName('rss'); 550 | var getFeedTag = oDOM.getElementsByTagName('feed'); 551 | 552 | if (getRssTag.length > 0 || getFeedTag.length > 0) { 553 | 554 | if (getRssTag.length > 0) { 555 | var getChannelTag = getRssTag['0'].getElementsByTagName('channel'); 556 | } else if (getFeedTag.length > 0) { 557 | var getChannelTag = getFeedTag['0']; 558 | } 559 | 560 | if (getChannelTag !== false) { 561 | isFound = true; 562 | 563 | feed = { 564 | type: '', 565 | url: feed_url, 566 | title: feed_url 567 | }; 568 | 569 | return feed; 570 | } 571 | } 572 | } 573 | } 574 | } 575 | 576 | return feed; 577 | } -------------------------------------------------------------------------------- /js/popup.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { 3 | var tab = tabs[0]; 4 | var url = tab.url; 5 | 6 | getFeedsURLs(url, function(feeds){ 7 | 8 | if (feeds.length > 0) { 9 | var html = ''; 13 | html += ''+feeds[i].title+''; 14 | html += ''+truncate(feeds[i].url, 50)+''; 15 | html += ' | '; 16 | html += ''; 17 | html += 'Copy URL'; 18 | html += ' | '; 19 | html += '