├── .gitignore ├── LICENSE.md ├── README.md ├── application ├── apps-ads.txt ├── assets │ ├── css │ │ ├── grid.css │ │ └── main.css │ ├── fonts │ │ └── Roboto-Regular.ttf │ ├── icons │ │ ├── 23EF.svg │ │ ├── 25B6.svg │ │ ├── 2605.svg │ │ ├── 2B07.svg │ │ ├── E0AB.svg │ │ ├── E1D8.svg │ │ ├── E24F.svg │ │ ├── E252.svg │ │ ├── E253.svg │ │ ├── E261.svg │ │ ├── E264.svg │ │ ├── E269.svg │ │ ├── back.svg │ │ ├── cancel.svg │ │ ├── clock.svg │ │ ├── delete.svg │ │ ├── eye.svg │ │ ├── favicon.ico │ │ ├── icon-112-112.png │ │ ├── icon-56-56.png │ │ ├── icon-56-56.svg │ │ ├── id.svg │ │ ├── image.svg │ │ ├── intro.png │ │ ├── intro.svg │ │ ├── link.svg │ │ ├── list.svg │ │ ├── offline.svg │ │ ├── online.svg │ │ ├── option.svg │ │ ├── person.svg │ │ ├── play.svg │ │ ├── reblog.svg │ │ ├── save.svg │ │ ├── select.svg │ │ └── sleep.svg │ └── js │ │ ├── helper.js │ │ ├── kaiads.v5.min.js │ │ ├── mastodon.js │ │ └── youtube.js ├── index.html ├── index.js ├── manifest.webapp ├── manifest.webmanifest ├── sw.js └── worker.js ├── docs ├── E264.ff3d42a6.svg ├── Roboto-Regular.4f1a9903.ttf ├── assets │ ├── fonts │ │ └── Roboto-Regular.ttf │ ├── icons │ │ ├── 23EF.svg │ │ ├── 25B6.svg │ │ ├── 2605.svg │ │ ├── 2B07.svg │ │ ├── E0AB.svg │ │ ├── E1D8.svg │ │ ├── E24F.svg │ │ ├── E252.svg │ │ ├── E253.svg │ │ ├── E261.svg │ │ ├── E264.svg │ │ ├── E269.svg │ │ ├── back.svg │ │ ├── cancel.svg │ │ ├── clock.svg │ │ ├── delete.svg │ │ ├── eye.svg │ │ ├── favicon.ico │ │ ├── icon-112-112.png │ │ ├── icon-56-56.png │ │ ├── icon-56-56.svg │ │ ├── id.svg │ │ ├── image.svg │ │ ├── intro.png │ │ ├── intro.svg │ │ ├── link.svg │ │ ├── list.svg │ │ ├── offline.svg │ │ ├── online.svg │ │ ├── option.svg │ │ ├── person.svg │ │ ├── play.svg │ │ ├── reblog.svg │ │ ├── save.svg │ │ ├── select.svg │ │ └── sleep.svg │ └── js │ │ └── kaiads.v5.min.js ├── favicon.3da022ef.ico ├── file-list.json ├── icon-112-112.185b7f3e.png ├── icon-56-56.bda5227e.png ├── index.339e55c4.js ├── index.5671dcd7.css ├── index.63982eba.css ├── index.8d33ff60.js ├── index.f0337e49.js ├── index.html ├── manifest.webapp ├── manifest.webmanifest ├── reblog.a18be2b5.svg ├── sw.js └── worker.8ac5962b.js ├── example.opml ├── example ├── example.opml ├── example_video.xml └── how-to.xml ├── generate_file_list.sh ├── image ├── kaios-banner.png ├── mockup.png ├── mockup.xcf └── screenshoot.png ├── package-lock.json ├── package.json └── proxy.php /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | node_modules/* 3 | build 4 | build/* 5 | .parcel-cache 6 | node_modules 7 | node_modules/* 8 | .parcel-cache 9 | build/* 10 | dist/* 11 | dist 12 | build 13 | .cache 14 | .env 15 | webpage-uploader.sh 16 | build.sh 17 | .gitignore 18 | webpage 19 | webpage/* 20 | .gitignore 21 | 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](/application/assets/icons/icon-112-112.png) 2 | 3 | # Feedolin 4 | 5 | ![badge-release](https://img.shields.io/github/v/release/strukturart/feedolin?include_prereleases&style=plastic) 6 | [![badge-bhackers](https://img.shields.io/badge/bHackers-bHackerStore-orange)](https://store.bananahackers.net/#feedolin) 7 | ![badge-downloads](https://img.shields.io/github/downloads/strukturart/feedolin/total) 8 | 9 | Feedolin is an RSS / Atom reader and podcast player. It is intended for users who already use an rss reader client and want to read their feeds on a KaiOS device. the list of subscribed websites / podcasts is managed locally or online in an opml. 10 | 11 | **NEW** 12 | 13 | Now it is also possible to load mastodon content, in the settings of the app you will find a login button with which you can log in with your mastodon account. 14 | 15 | ## RSS/Atom/Mastodon reader and Podcastplayer for KaiOS 16 | 17 | - read you favorit rss/atom feeds 18 | - stream your podcasts 19 | - read mastodon publice & home timeline 20 | - open rss feedlink 21 | - offline use (caching) 22 | 23 | ![feedolin_mockup](/image/mockup.png) 24 | 25 | ## Installation 26 | 27 | You have to save the list of urls in a .opml file as in the example. 28 | In the settings you can set where the app should get this file from. 29 | It can be stored locally or online on a server. 30 | 31 | if you want to show mastodon public timelines you have to set the type="mastodon" in the link list 32 | For example: 33 | `` 34 | 35 | https://raw.githubusercontent.com/strukturart/rss-reader/master/example.opml 36 |
37 | 38 | ` 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ` 60 | 61 | 62 |
63 | 64 | - 5 feeds will be loaded per source, if you want more you can set it with this attribute: maxEpisodes="8" 65 | - To divide the individual feeds into categories, you have to create nested outlines. In the app, you can then jump between the categories using the key: left/right or swipe 66 | 67 | ## How to install 68 | 69 | - KaiOs Store 70 | - Sideloading step-by-step article by martinkaptein 71 | 72 | You can download the latest version from the Releases page. 73 | The app is not auto-updating. To update it, you have to follow the same steps you took when installing it. 74 | 75 | ## create your own 76 | 77 | `npm -i` 78 | 79 | to use another mastodon instance you need to save the necessary data in application/.env, don't forget to include it in your .gitignore. 80 | https://docs.joinmastodon.org/methods/apps/

81 | 82 | ``` 83 | 84 | clientId=xx 85 | clientSecret=xxx 86 | redirect=https://xx 87 | 88 | ``` 89 | 90 | ## Mastodon 91 | 92 | Originally I only wanted to write an rss reader for KaiOS, now I have decided to connect the fediverse as well. a mastodon login enables you to display the mastodon home timeline. in mastodon you have the option of subscribing to other sources/instances of fediverse, which will then also find their way to your feature phone. 93 | 94 | ## How to use 95 | 96 | - Enter to show full news 97 | - Backspace to go back 98 | - #-key volume 99 | - \*-key open audioplayer 100 | - streaming audio seeking: cursor left/right 101 | 102 | ## Tips 103 | 104 | RSS-Feed from public Youtube/Soundcloud/Instagram/Twitter..... 105 | 106 | - https://danielmiessler.com/blog/rss-feed-youtube-channel/ 107 | - http://tips.slaw.ca/2019/technology/rss-feed-for-a-youtube-channel-or-playlist/ 108 | - https://rss.app/rss-feed/create-instagram-rss-feed 109 | 110 | ## Credits 111 | 112 | - bananahackers community 💘 113 | 114 | ## Donation 115 | 116 | Donate using Liberapay 117 | -------------------------------------------------------------------------------- /application/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/application/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /application/assets/icons/23EF.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 44 | 47 | 56 | 68 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /application/assets/icons/25B6.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 44 | 47 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /application/assets/icons/2605.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /application/assets/icons/2B07.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/assets/icons/E0AB.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /application/assets/icons/E24F.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 44 | 47 | 56 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /application/assets/icons/E252.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /application/assets/icons/E253.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /application/assets/icons/E261.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /application/assets/icons/E264.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 32 | 34 | 38 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /application/assets/icons/E269.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /application/assets/icons/back.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 17 | 21 | 22 | 25 | 29 | 30 | 31 | 49 | 56 | 59 | 64 | 65 | -------------------------------------------------------------------------------- /application/assets/icons/cancel.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 34 | 36 | 38 | 40 | 47 | 51 | 52 | -------------------------------------------------------------------------------- /application/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | clock 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /application/assets/icons/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 33 | 35 | 37 | 39 | 46 | 49 | 51 | 56 | 65 | 70 | 82 | 83 | 85 | 94 | 103 | 115 | 127 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /application/assets/icons/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 39 | 41 | 46 | 49 | 55 | 58 | 70 | 77 | 88 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /application/assets/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/application/assets/icons/favicon.ico -------------------------------------------------------------------------------- /application/assets/icons/icon-112-112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/application/assets/icons/icon-112-112.png -------------------------------------------------------------------------------- /application/assets/icons/icon-56-56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/application/assets/icons/icon-56-56.png -------------------------------------------------------------------------------- /application/assets/icons/id.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 32 | 39 | # 50 | 51 | -------------------------------------------------------------------------------- /application/assets/icons/image.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 41 | 47 | 50 | 62 | 71 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /application/assets/icons/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/application/assets/icons/intro.png -------------------------------------------------------------------------------- /application/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /application/assets/icons/list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 41 | 43 | 48 | 52 | 58 | 61 | 64 | 67 | 77 | 86 | 95 | 96 | 99 | 109 | 110 | 113 | 123 | 124 | 125 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /application/assets/icons/offline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 18 | 36 | 40 | 44 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /application/assets/icons/online.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 18 | 36 | 40 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /application/assets/icons/option.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 39 | 41 | 46 | 49 | 55 | 61 | 67 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /application/assets/icons/person.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 17 | 21 | 22 | 25 | 29 | 30 | 31 | 49 | 56 | 59 | 67 | 75 | 83 | 91 | 99 | 101 | 109 | 112 | 115 | 122 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /application/assets/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 32 | 39 | 42 | 44 | 47 | 48 | 49 | 52 | 54 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /application/assets/icons/reblog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /application/assets/icons/save.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 34 | 36 | 38 | 40 | 47 | 50 | 62 | 74 | 86 | 95 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /application/assets/icons/select.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 41 | 47 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /application/assets/icons/sleep.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 41 | 47 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /application/assets/js/mastodon.js: -------------------------------------------------------------------------------- 1 | export let load_context = (url, id, mode) => { 2 | let a = JSON.parse(localStorage.getItem("oauth_auth")); 3 | let accessToken = a.access_token; 4 | 5 | let w = { 6 | Authorization: `Bearer ${accessToken}`, 7 | "Content-Type": "application/json", 8 | }; 9 | if (mode == "public") { 10 | w = { 11 | "Content-Type": "application/json", 12 | }; 13 | } 14 | return fetch(url + "/api/v1/statuses/" + id + "/context", { 15 | headers: w, 16 | }) 17 | .then((response) => { 18 | if (!response.ok) { 19 | console.log("Network response was not OK"); 20 | } 21 | return response.json(); 22 | }) 23 | .then((data) => { 24 | return data; 25 | }); 26 | }; 27 | 28 | export let reblog = (url, id) => { 29 | let a = JSON.parse(localStorage.getItem("oauth_auth")); 30 | let accessToken = a.access_token; 31 | 32 | let w = { 33 | Authorization: `Bearer ${accessToken}`, 34 | "Content-Type": "application/json", 35 | }; 36 | 37 | return new Promise((resolve, reject) => { 38 | fetch(url + "/api/v1/statuses/" + id + "/reblog", { 39 | method: "POST", 40 | headers: w, 41 | }) 42 | .then((response) => { 43 | if (!response.ok) { 44 | console.log("Network response was not OK"); 45 | reject(new Error("Network response was not OK")); 46 | } 47 | return response.json(); 48 | }) 49 | .then((data) => { 50 | resolve(data); 51 | }) 52 | .catch((error) => { 53 | reject(error); 54 | }); 55 | }); 56 | }; 57 | 58 | export let favourite = (url, id) => { 59 | let a = JSON.parse(localStorage.getItem("oauth_auth")); 60 | let accessToken = a.access_token; 61 | 62 | let w = { 63 | Authorization: `Bearer ${accessToken}`, 64 | "Content-Type": "application/json", 65 | }; 66 | 67 | return new Promise((resolve, reject) => { 68 | fetch(url + "/api/v1/statuses/" + id + "/favourite", { 69 | method: "POST", 70 | headers: w, 71 | }) 72 | .then((response) => { 73 | if (!response.ok) { 74 | console.log("Network response was not OK"); 75 | reject(new Error("Network response was not OK")); 76 | } 77 | return response.json(); 78 | }) 79 | .then((data) => { 80 | resolve(data); 81 | }) 82 | .catch((error) => { 83 | reject(error); 84 | }); 85 | }); 86 | }; 87 | 88 | export let mastodon_account_info = async (url, accessToken) => { 89 | const response = await fetch(url + "/api/v1/accounts/verify_credentials", { 90 | headers: { 91 | Authorization: `Bearer ${accessToken}`, 92 | "Content-Type": "application/json", 93 | }, 94 | }); 95 | if (!response.ok) { 96 | console.log("Network response was not OK"); 97 | } 98 | const data_1 = await response.json(); 99 | return data_1; 100 | }; 101 | -------------------------------------------------------------------------------- /application/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | feedolin 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 |
27 |
28 | 29 | 30 |
31 | 32 |
33 |
34 |
35 |
36 |
37 |
38 | 39 | 40 | 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /application/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.18", 3 | "name": "feedolin", 4 | "description": "Feedolin is an RSS / Atom / Mastodon reader and podcast player. It is intended for users who already use an rss reader client and want to read their feeds on a kaios device. the list of subscribed websites / podcasts is managed locally or online in an opml file.", 5 | "launch_path": "/index.html", 6 | "type": "privileged", 7 | "fullscreen": "true", 8 | "categories": ["News","Social"], 9 | "userAgentInfo": "Feedolin written by strukturart@gmail.com", 10 | 11 | 12 | "icons": { 13 | "56": "/assets/icons/icon-56-56.png", 14 | "112": "/assets/icons/icon-112-112.png" 15 | }, 16 | 17 | "developer": { 18 | "name": "strukturart", 19 | "url": "https://github.com/strukturart/feedolin" 20 | }, 21 | 22 | "dependencies": { 23 | "ads-sdk": "1.5.8" 24 | }, 25 | 26 | "locales": { 27 | "en-US": { 28 | "name": "feedolin", 29 | "subtitle": "Feedolin is an RSS / Atom reader and podcast player. ", 30 | "description": "Feedolin is an RSS / Atom / Mastodon reader and podcast player. It is intended for users who already use an rss reader client and want to read their feeds on a kaios device. the list of subscribed websites / podcasts is managed locally or online in an opml file." 31 | } 32 | }, 33 | "default_locale": "en-US", 34 | 35 | "messages": [ 36 | { 37 | "alarm": "/index.html" 38 | }, 39 | { 40 | "notification": "/index.html" 41 | }, 42 | { "serviceworker-notification": "/index.html" }, 43 | { "activity": "/index.html" } 44 | ], 45 | 46 | 47 | "redirects": [ 48 | { 49 | "from": "https://feedolin.strukturart.com/", 50 | "to": "/index.html" 51 | } 52 | ], 53 | 54 | "serviceworker": { 55 | "script_url": "sw.js" 56 | }, 57 | 58 | 59 | 60 | "permissions": { 61 | "serviceworker": { 62 | "description": "Needed for assocating service worker" 63 | }, 64 | "mobiledata": {}, 65 | "wifidata": {}, 66 | "calllog": {}, 67 | "feature-detection": { 68 | "description": "query which keys are available" 69 | }, 70 | 71 | "device-storage:sdcard": { 72 | "description": "Read/Write from/to sd-card", 73 | "access": "readwrite" 74 | }, 75 | 76 | 77 | "audio-channel-content": { 78 | "description": "Needed to play this app's audio content on the content channel" 79 | }, 80 | "desktop-notification": { 81 | "description": "Needed to fire system notifications" 82 | }, 83 | 84 | "volumemanager": {}, 85 | "browser": {}, 86 | "spatialnavigation-app-manage": { 87 | "navigator.spatialNavigationEnabled": false 88 | }, 89 | 90 | "systemXHR": { 91 | "description": "Required to load remote content" 92 | }, 93 | "alarms": { 94 | "description": "Required to schedule alarms" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /application/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Feedolin", 3 | "id": "Feedolin", 4 | "description": "Feedolin is an RSS / Atom / Mastodon reader and podcast player. It is intended for users who already use an rss reader client and want to read their feeds on a kaios device. the list of subscribed websites / podcasts is managed locally or online in an opml file.", 5 | "lang": "en-US", 6 | "start_url": "/index.html", 7 | "short_name": "Feedolin", 8 | "categories": ["News"], 9 | "display": "standalone", 10 | "theme_color": "#ffffff", 11 | "userAgentInfo": "Feedolin written by strukturart@gmail.com", 12 | 13 | "icons": [ 14 | { 15 | "src": "assets/icons/icon-56-56.png", 16 | "type": "image/png", 17 | "sizes": "56x56" 18 | }, 19 | { 20 | "src": "assets/icons/icon-112-112.png", 21 | "type": "image/png", 22 | "sizes": "112x112" 23 | } 24 | ], 25 | 26 | "b2g_features": { 27 | "version": "1.8.140", 28 | "id": "feedolin", 29 | "subtitle": "RSS Reader and Mastodon Reader", 30 | "core": true, 31 | "type": "privileged", 32 | "display": "fullscreen", 33 | "developer": { 34 | "name": "strukturart", 35 | "url": "https://github.com/strukturart/feedolin" 36 | }, 37 | 38 | "dependencies": { 39 | "ads-sdk": "1.5.8" 40 | }, 41 | 42 | "messages": [ 43 | { "serviceworker-notification": "index.html" }, 44 | { "activity": "/index.html" } 45 | ], 46 | 47 | "activities": { 48 | "feedolin": {} 49 | }, 50 | 51 | "serviceworker": { 52 | "script_url": "sw.js" 53 | }, 54 | 55 | "permissions": { 56 | "desktop-notification": { 57 | "description": "Needed to fire system notifications" 58 | }, 59 | 60 | "feature-detection": { 61 | "description": "query which keys are available" 62 | }, 63 | "mobiledata": {}, 64 | "wifidata": {}, 65 | "calllog": {}, 66 | 67 | "audio-channel-content": { 68 | "description": "Allow background audio playback." 69 | }, 70 | "volumemanager": { "description": "" }, 71 | "device-storage:sdcard": { 72 | "description": "Read/Write from/to sd-card", 73 | "access": "readwrite" 74 | }, 75 | "spatialnavigation-app-manage": { 76 | "navigator.spatialNavigationEnabled": false 77 | }, 78 | "systemXHR": { 79 | "description": "Required to load remote content" 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /application/sw.js: -------------------------------------------------------------------------------- 1 | const sw_channel = new BroadcastChannel("sw-messages"); 2 | sw_channel.addEventListener("message", (event) => { 3 | sw_channel.postMessage({ 4 | test: event.data.test, 5 | }); 6 | }); 7 | self.addEventListener("systemmessage", async (evt) => { 8 | // Store evt data early 9 | let activityData; 10 | 11 | if (evt.name === "activity") { 12 | try { 13 | const handler = evt.data.webActivityRequestHandler(); 14 | 15 | const { name: activityName, data } = handler.source; 16 | 17 | // Store data for later use 18 | activityData = data; 19 | 20 | if (activityData.name === "feedolin") { 21 | sw_channel.postMessage({ 22 | oauth_success: activityData.data, 23 | }); 24 | } 25 | } catch (error) { 26 | console.error("Error handling system message:", error); 27 | } 28 | } 29 | }); 30 | 31 | const userAgent = navigator.userAgent || ""; 32 | 33 | if (userAgent && !userAgent.includes("KAIOS")) { 34 | const CACHE_NAME = "pwa-cache-v0.1217"; 35 | const FILE_LIST_URL = "/file-list.json"; // URL of the JSON file containing the array of files 36 | 37 | self.addEventListener("install", (event) => { 38 | event.waitUntil( 39 | caches 40 | .open(CACHE_NAME) 41 | .then((cache) => { 42 | console.log("Opened cache"); 43 | 44 | // Fetch the file list JSON and cache the URLs 45 | return fetch(FILE_LIST_URL) 46 | .then((response) => { 47 | if (!response.ok) { 48 | throw new Error(`HTTP error! Status: ${response.status}`); 49 | } 50 | return response.json(); // Parse the JSON response 51 | }) 52 | .then((urlsToCache) => { 53 | // Ensure urlsToCache is an array 54 | if (Array.isArray(urlsToCache)) { 55 | return Promise.all( 56 | urlsToCache.map((url) => 57 | cache.add(url).catch((error) => { 58 | console.error(`Failed to cache ${url}:`, error); 59 | }) 60 | ) 61 | ); 62 | } else { 63 | console.error("Fetched data is not an array:", urlsToCache); 64 | } 65 | }); 66 | }) 67 | .then(() => { 68 | return self.skipWaiting(); // Skip waiting and activate the new SW immediately 69 | }) 70 | ); 71 | }); 72 | 73 | self.addEventListener("activate", (event) => { 74 | const cacheWhitelist = [CACHE_NAME]; 75 | event.waitUntil( 76 | caches.keys().then((cacheNames) => { 77 | return Promise.all( 78 | cacheNames.map((cacheName) => { 79 | if (!cacheWhitelist.includes(cacheName)) { 80 | return caches.delete(cacheName); 81 | } 82 | }) 83 | ); 84 | }) 85 | ); 86 | }); 87 | 88 | // Serve files from cache when offline 89 | self.addEventListener("fetch", (event) => { 90 | event.respondWith( 91 | caches.match(event.request).then((response) => { 92 | // If the request is in the cache, return it. Otherwise, fetch from the network. 93 | return response || fetch(event.request); 94 | }) 95 | ); 96 | }); 97 | } 98 | -------------------------------------------------------------------------------- /application/worker.js: -------------------------------------------------------------------------------- 1 | let timerId = null; 2 | let endTime = 0; 3 | 4 | self.onmessage = function (event) { 5 | const { action, duration } = event.data; 6 | 7 | if (action === "start") { 8 | endTime = Date.now() + duration; 9 | clearInterval(timerId); // Clear any previous timer 10 | timerId = setInterval(() => { 11 | const remainingTime = endTime - Date.now(); 12 | self.postMessage({ remaining: remainingTime }); 13 | if (remainingTime <= 0) { 14 | clearInterval(timerId); 15 | timerId = null; // Reset timerId when the timer completes 16 | self.postMessage({ action: "stop" }); 17 | } 18 | }, 1000); // Check every second 19 | } else if (action === "stop") { 20 | clearInterval(timerId); 21 | timerId = null; // Reset timerId so that a new "start" message works correctly 22 | endTime = 0; // Reset endTime to prevent continuing after stopping 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /docs/E264.ff3d42a6.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/Roboto-Regular.4f1a9903.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/Roboto-Regular.4f1a9903.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /docs/assets/icons/23EF.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 44 | 47 | 56 | 68 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/assets/icons/25B6.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 44 | 47 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /docs/assets/icons/2605.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/assets/icons/2B07.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/assets/icons/E0AB.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/assets/icons/E24F.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 44 | 47 | 56 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /docs/assets/icons/E252.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/assets/icons/E253.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/assets/icons/E261.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/assets/icons/E264.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 32 | 34 | 38 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/assets/icons/E269.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/assets/icons/back.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 17 | 21 | 22 | 25 | 29 | 30 | 31 | 49 | 56 | 59 | 64 | 65 | -------------------------------------------------------------------------------- /docs/assets/icons/cancel.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 34 | 36 | 38 | 40 | 47 | 51 | 52 | -------------------------------------------------------------------------------- /docs/assets/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | clock 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/assets/icons/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 33 | 35 | 37 | 39 | 46 | 49 | 51 | 56 | 65 | 70 | 82 | 83 | 85 | 94 | 103 | 115 | 127 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /docs/assets/icons/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 39 | 41 | 46 | 49 | 55 | 58 | 70 | 77 | 88 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /docs/assets/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/assets/icons/favicon.ico -------------------------------------------------------------------------------- /docs/assets/icons/icon-112-112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/assets/icons/icon-112-112.png -------------------------------------------------------------------------------- /docs/assets/icons/icon-56-56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/assets/icons/icon-56-56.png -------------------------------------------------------------------------------- /docs/assets/icons/id.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 32 | 39 | # 50 | 51 | -------------------------------------------------------------------------------- /docs/assets/icons/image.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 41 | 47 | 50 | 62 | 71 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /docs/assets/icons/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/assets/icons/intro.png -------------------------------------------------------------------------------- /docs/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 38 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /docs/assets/icons/list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 41 | 43 | 48 | 52 | 58 | 61 | 64 | 67 | 77 | 86 | 95 | 96 | 99 | 109 | 110 | 113 | 123 | 124 | 125 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /docs/assets/icons/offline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 18 | 36 | 40 | 44 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /docs/assets/icons/online.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 18 | 36 | 40 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /docs/assets/icons/option.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 39 | 41 | 46 | 49 | 55 | 61 | 67 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/assets/icons/person.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 17 | 21 | 22 | 25 | 29 | 30 | 31 | 49 | 56 | 59 | 67 | 75 | 83 | 91 | 99 | 101 | 109 | 112 | 115 | 122 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /docs/assets/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 32 | 39 | 42 | 44 | 47 | 48 | 49 | 52 | 54 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/assets/icons/reblog.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/assets/icons/save.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 34 | 36 | 38 | 40 | 47 | 50 | 62 | 74 | 86 | 95 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /docs/assets/icons/select.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 41 | 47 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/assets/icons/sleep.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 41 | 47 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/favicon.3da022ef.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/favicon.3da022ef.ico -------------------------------------------------------------------------------- /docs/file-list.json: -------------------------------------------------------------------------------- 1 | [ 2 | "/index.html", 3 | "/index.63982eba.css", 4 | "/index.5671dcd7.css", 5 | "/manifest.webapp", 6 | "/index.339e55c4.js", 7 | "/icon-56-56.bda5227e.png", 8 | "/reblog.a18be2b5.svg", 9 | "/Roboto-Regular.4f1a9903.ttf", 10 | "/index.8d33ff60.js", 11 | "/icon-112-112.185b7f3e.png", 12 | "/E264.ff3d42a6.svg", 13 | "/file-list.json", 14 | "/sw.js", 15 | "/worker.8ac5962b.js", 16 | "/index.f0337e49.js", 17 | "/favicon.3da022ef.ico", 18 | "/manifest.webmanifest", 19 | "/assets/icons/save.svg", 20 | "/assets/icons/intro.svg", 21 | "/assets/icons/2605.svg", 22 | "/assets/icons/E269.svg", 23 | "/assets/icons/E1D8.svg", 24 | "/assets/icons/option.svg", 25 | "/assets/icons/intro.png", 26 | "/assets/icons/icon-112-112.png", 27 | "/assets/icons/sleep.svg", 28 | "/assets/icons/delete.svg", 29 | "/assets/icons/online.svg", 30 | "/assets/icons/reblog.svg", 31 | "/assets/icons/icon-56-56.png", 32 | "/assets/icons/E252.svg", 33 | "/assets/icons/person.svg", 34 | "/assets/icons/select.svg", 35 | "/assets/icons/23EF.svg", 36 | "/assets/icons/E261.svg", 37 | "/assets/icons/25B6.svg", 38 | "/assets/icons/cancel.svg", 39 | "/assets/icons/eye.svg", 40 | "/assets/icons/icon-56-56.svg", 41 | "/assets/icons/favicon.ico", 42 | "/assets/icons/link.svg", 43 | "/assets/icons/E0AB.svg", 44 | "/assets/icons/back.svg", 45 | "/assets/icons/2B07.svg", 46 | "/assets/icons/E253.svg", 47 | "/assets/icons/image.svg", 48 | "/assets/icons/E24F.svg", 49 | "/assets/icons/clock.svg", 50 | "/assets/icons/play.svg", 51 | "/assets/icons/list.svg", 52 | "/assets/icons/id.svg", 53 | "/assets/icons/E264.svg", 54 | "/assets/icons/offline.svg", 55 | "/assets/fonts/Roboto-Regular.ttf", 56 | "/assets/js/kaiads.v5.min.js" 57 | ] 58 | -------------------------------------------------------------------------------- /docs/icon-112-112.185b7f3e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/icon-112-112.185b7f3e.png -------------------------------------------------------------------------------- /docs/icon-56-56.bda5227e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/docs/icon-56-56.bda5227e.png -------------------------------------------------------------------------------- /docs/index.5671dcd7.css: -------------------------------------------------------------------------------- 1 | .grid-col-1{min-width:10px;max-width:10px}.grid-col-2{min-width:20px;max-width:20px}.grid-col-3{min-width:30px;max-width:30px}.grid-col-4{min-width:40px;max-width:40px}.grid-col-5{min-width:50px;max-width:50px}.grid-col-6{min-width:60px;max-width:60px}.grid-col-7{min-width:70px;max-width:70px}.grid-col-8{min-width:80px;max-width:80px}.grid-col-9{min-width:90px;max-width:90px}.grid-col-10{min-width:100px;max-width:100px}.grid-col-11{min-width:110px;max-width:110px}.grid-col-12{min-width:120px;max-width:120px}.grid-col-13{min-width:130px;max-width:130px}.grid-col-14{min-width:140px;max-width:140px}.grid-col-15{min-width:150px;max-width:150px}.grid-col-16{min-width:160px;max-width:160px}.grid-col-17{min-width:170px;max-width:170px}.grid-col-18{min-width:180px;max-width:180px}.grid-col-19{min-width:190px;max-width:190px}.grid-col-20{min-width:200px;max-width:200px}.grid-col-21{min-width:210px;max-width:210px}.grid-col-22{min-width:220px;max-width:220px}.grid-col-23{min-width:230px;max-width:230px}.grid-col-24{min-width:240px;max-width:240px}.grid-col-25{min-width:250px;max-width:250px}.grid-col-26{min-width:260px;max-width:260px}.grid-col-27{min-width:270px;max-width:270px}.grid-col-28{min-width:280px;max-width:280px}.grid-col-29{min-width:290px;max-width:290px}.grid-col-30{min-width:300px;max-width:300px}.grid-col-31{min-width:310px;max-width:310px}.grid-col-32{min-width:320px;max-width:320px}.grid-col-33{min-width:330px;max-width:330px}.grid-col-34{min-width:340px;max-width:340px}.grid-col-35{min-width:350px;max-width:350px}.grid-col-36{min-width:360px;max-width:360px}.grid-col-37{min-width:370px;max-width:370px}.grid-col-38{min-width:380px;max-width:380px}.grid-col-39{min-width:390px;max-width:390px}.grid-col-40{min-width:400px;max-width:400px}.grid-col-41{min-width:410px;max-width:410px}.grid-col-42{min-width:420px;max-width:420px}.grid-col-43{min-width:430px;max-width:430px}.grid-col-44{min-width:440px;max-width:440px}.grid-col-45{min-width:450px;max-width:450px}.grid-col-46{min-width:460px;max-width:460px}.grid-col-47{min-width:470px;max-width:470px}.grid-col-48{min-width:480px;max-width:480px}.grid-col-49{min-width:490px;max-width:490px}.grid-col-50{min-width:500px;max-width:500px}.grid-col-51{min-width:510px;max-width:510px}.grid-col-52{min-width:520px;max-width:520px}.grid-col-53{min-width:530px;max-width:530px}.grid-col-54{min-width:540px;max-width:540px}.grid-col-55{min-width:550px;max-width:550px}.grid-col-56{min-width:560px;max-width:560px}.grid-col-57{min-width:570px;max-width:570px}.grid-col-58{min-width:580px;max-width:580px}.grid-col-59{min-width:590px;max-width:590px}.grid-col-60{min-width:600px;max-width:600px}.grid-col-61{min-width:610px;max-width:610px}.grid-col-62{min-width:620px;max-width:620px}.grid-col-63{min-width:630px;max-width:630px}.grid-col-64{min-width:640px;max-width:640px}.grid-col-65{min-width:650px;max-width:650px}.grid-col-66{min-width:660px;max-width:660px}.grid-col-67{min-width:670px;max-width:670px}.grid-col-68{min-width:680px;max-width:680px}.grid-col-69{min-width:690px;max-width:690px}.grid-col-70{min-width:700px;max-width:700px}.grid-col-71{min-width:710px;max-width:710px}.grid-col-72{min-width:720px;max-width:720px}.grid-col-73{min-width:730px;max-width:730px}.grid-col-74{min-width:740px;max-width:740px}.grid-col-75{min-width:750px;max-width:750px}.grid-col-76{min-width:760px;max-width:760px}.grid-col-77{min-width:770px;max-width:770px}.grid-col-78{min-width:780px;max-width:780px}.grid-col-79{min-width:790px;max-width:790px}.grid-col-80{min-width:800px;max-width:800px}.grid-col-81{min-width:810px;max-width:810px}.grid-col-82{min-width:820px;max-width:820px}.grid-col-83{min-width:830px;max-width:830px}.grid-col-84{min-width:840px;max-width:840px}.grid-col-85{min-width:850px;max-width:850px}.grid-col-86{min-width:860px;max-width:860px}.grid-col-87{min-width:870px;max-width:870px}.grid-col-88{min-width:880px;max-width:880px}.grid-col-89{min-width:890px;max-width:890px}.grid-col-90{min-width:900px;max-width:900px}.grid-col-91{min-width:910px;max-width:910px}.grid-col-92{min-width:920px;max-width:920px}.grid-col-93{min-width:930px;max-width:930px}.grid-col-94{min-width:940px;max-width:940px}.grid-col-95{min-width:950px;max-width:950px}.grid-col-96{min-width:960px;max-width:960px}.text-center{text-align:center}.flex{-moz-box-orient:horizontal;-moz-box-direction:normal;flex-flow:wrap;display:flex}.flex-column{-moz-box-orient:vertical;-moz-box-direction:normal;flex-direction:column}.justify-content-start{-moz-box-align:start;align-items:flex-start}.justify-content-end{-moz-box-pack:end;justify-content:flex-end}.algin-item-start{-moz-box-align:start;align-items:flex-start}.algin-item-end{-moz-box-align:end;align-items:flex-end}.align-item-center{-moz-box-align:center;align-items:center}.justify-content-spacebetween{-moz-box-pack:justify;justify-content:space-between}.justify-content-spacearound{align-content:space-around;justify-content:space-around}.justify-content-center{-moz-box-pack:center;justify-content:center}.width-100{min-width:100vw;max-width:100vw}.width-90{min-width:90vw;max-width:90vw}.height-100{min-height:100vh}.width-50{min-width:50%}.width-30{min-width:30%;max-width:30%}.center{transform:translate(50%)}.block{display:block} -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | feedolin
-------------------------------------------------------------------------------- /docs/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.18", 3 | "name": "feedolin", 4 | "description": "Feedolin is an RSS / Atom / Mastodon reader and podcast player. It is intended for users who already use an rss reader client and want to read their feeds on a kaios device. the list of subscribed websites / podcasts is managed locally or online in an opml file.", 5 | "launch_path": "/index.html", 6 | "type": "privileged", 7 | "fullscreen": "true", 8 | "categories": ["News","Social"], 9 | "userAgentInfo": "Feedolin written by strukturart@gmail.com", 10 | 11 | 12 | "icons": { 13 | "56": "/assets/icons/icon-56-56.png", 14 | "112": "/assets/icons/icon-112-112.png" 15 | }, 16 | 17 | "developer": { 18 | "name": "strukturart", 19 | "url": "https://github.com/strukturart/feedolin" 20 | }, 21 | 22 | "dependencies": { 23 | "ads-sdk": "1.5.8" 24 | }, 25 | 26 | "locales": { 27 | "en-US": { 28 | "name": "feedolin", 29 | "subtitle": "Feedolin is an RSS / Atom reader and podcast player. ", 30 | "description": "Feedolin is an RSS / Atom / Mastodon reader and podcast player. It is intended for users who already use an rss reader client and want to read their feeds on a kaios device. the list of subscribed websites / podcasts is managed locally or online in an opml file." 31 | } 32 | }, 33 | "default_locale": "en-US", 34 | 35 | "messages": [ 36 | { 37 | "alarm": "/index.html" 38 | }, 39 | { 40 | "notification": "/index.html" 41 | }, 42 | { "serviceworker-notification": "/index.html" }, 43 | { "activity": "/index.html" } 44 | ], 45 | 46 | 47 | "redirects": [ 48 | { 49 | "from": "https://feedolin.strukturart.com/", 50 | "to": "/index.html" 51 | } 52 | ], 53 | 54 | "serviceworker": { 55 | "script_url": "sw.js" 56 | }, 57 | 58 | 59 | 60 | "permissions": { 61 | "serviceworker": { 62 | "description": "Needed for assocating service worker" 63 | }, 64 | "mobiledata": {}, 65 | "wifidata": {}, 66 | "calllog": {}, 67 | "feature-detection": { 68 | "description": "query which keys are available" 69 | }, 70 | 71 | "device-storage:sdcard": { 72 | "description": "Read/Write from/to sd-card", 73 | "access": "readwrite" 74 | }, 75 | 76 | 77 | "audio-channel-content": { 78 | "description": "Needed to play this app's audio content on the content channel" 79 | }, 80 | "desktop-notification": { 81 | "description": "Needed to fire system notifications" 82 | }, 83 | 84 | "volumemanager": {}, 85 | "browser": {}, 86 | "spatialnavigation-app-manage": { 87 | "navigator.spatialNavigationEnabled": false 88 | }, 89 | 90 | "systemXHR": { 91 | "description": "Required to load remote content" 92 | }, 93 | "alarms": { 94 | "description": "Required to schedule alarms" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /docs/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Feedolin", 3 | "id": "Feedolin", 4 | "description": "Feedolin is an RSS / Atom / Mastodon reader and podcast player. It is intended for users who already use an rss reader client and want to read their feeds on a kaios device. the list of subscribed websites / podcasts is managed locally or online in an opml file.", 5 | "lang": "en-US", 6 | "start_url": "/index.html", 7 | "short_name": "Feedolin", 8 | "categories": ["News"], 9 | "display": "standalone", 10 | "theme_color": "#ffffff", 11 | "userAgentInfo": "Feedolin written by strukturart@gmail.com", 12 | 13 | "icons": [ 14 | { 15 | "src": "assets/icons/icon-56-56.png", 16 | "type": "image/png", 17 | "sizes": "56x56" 18 | }, 19 | { 20 | "src": "assets/icons/icon-112-112.png", 21 | "type": "image/png", 22 | "sizes": "112x112" 23 | } 24 | ], 25 | 26 | "b2g_features": { 27 | "version": "1.8.140", 28 | "id": "feedolin", 29 | "subtitle": "RSS Reader and Mastodon Reader", 30 | "core": true, 31 | "type": "privileged", 32 | "display": "fullscreen", 33 | "developer": { 34 | "name": "strukturart", 35 | "url": "https://github.com/strukturart/feedolin" 36 | }, 37 | 38 | "dependencies": { 39 | "ads-sdk": "1.5.8" 40 | }, 41 | 42 | "messages": [ 43 | { "serviceworker-notification": "index.html" }, 44 | { "activity": "/index.html" } 45 | ], 46 | 47 | "activities": { 48 | "feedolin": {} 49 | }, 50 | 51 | "serviceworker": { 52 | "script_url": "sw.js" 53 | }, 54 | 55 | "permissions": { 56 | "desktop-notification": { 57 | "description": "Needed to fire system notifications" 58 | }, 59 | 60 | "feature-detection": { 61 | "description": "query which keys are available" 62 | }, 63 | "mobiledata": {}, 64 | "wifidata": {}, 65 | "calllog": {}, 66 | 67 | "audio-channel-content": { 68 | "description": "Allow background audio playback." 69 | }, 70 | "volumemanager": { "description": "" }, 71 | "device-storage:sdcard": { 72 | "description": "Read/Write from/to sd-card", 73 | "access": "readwrite" 74 | }, 75 | "spatialnavigation-app-manage": { 76 | "navigator.spatialNavigationEnabled": false 77 | }, 78 | "systemXHR": { 79 | "description": "Required to load remote content" 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /docs/reblog.a18be2b5.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/sw.js: -------------------------------------------------------------------------------- 1 | !function(){function e(e,t,n,r,a,o,s){try{var c=e[o](s),i=c.value}catch(e){n(e);return}c.done?t(i):Promise.resolve(i).then(r,a)}"function"==typeof SuppressedError&&SuppressedError;var t,n,r=new BroadcastChannel("sw-messages");r.addEventListener("message",function(e){r.postMessage({test:e.data.test})}),self.addEventListener("systemmessage",(t=function(e){var t,n;return function(e,t){var n,r,a,o={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]},s=Object.create(("function"==typeof Iterator?Iterator:Object).prototype);return s.next=c(0),s.throw=c(1),s.return=c(2),"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function c(c){return function(i){return function(c){if(n)throw TypeError("Generator is already executing.");for(;s&&(s=0,c[0]&&(o=0)),o;)try{if(n=1,r&&(a=2&c[0]?r.return:c[0]?r.throw||((a=r.return)&&a.call(r),0):r.next)&&!(a=a.call(r,c[1])).done)return a;switch(r=0,a&&(c=[2&c[0],a.value]),c[0]){case 0:case 1:a=c;break;case 4:return o.label++,{value:c[1],done:!1};case 5:o.label++,r=c[1],c=[0];continue;case 7:c=o.ops.pop(),o.trys.pop();continue;default:if(!(a=(a=o.trys).length>0&&a[a.length-1])&&(6===c[0]||2===c[0])){o=0;continue}if(3===c[0]&&(!a||c[1]>a[0]&&c[1] 2 | 3 | 4 | feedolin 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /example/example.opml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | feedolin 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/example_video.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Sample RSS Video 7 | http://rssvideoplayer.com/sample.xml 8 | en-us 9 | 10 | MP4 Video 11 | http://www.bigbuckbunny.org 12 | Sun, 01 Jun 2014 00:00:00 GMT 13 | Big Buck Bunny Animation - Mp4 video]]> 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example/how-to.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Sample RSS Video 7 | http://rssvideoplayer.com/sample.xml 8 | en-us 9 | 10 | A short guide to feedolin 11 | 12 | Sun, 01 Jun 2022 00:00:00 GMT 13 | Feedolin is RSS/Atom Feeds Reader. This allows you to read short versions of the website, listen to the podcast or watch YouTube channels.How do I add new content?]] 14 | What you can't do with this app]] 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /generate_file_list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set the output JSON file name 4 | OUTPUT_FILE="./docs/file-list.json" 5 | DOCS_DIR="./docs" 6 | 7 | # Start the JSON array 8 | echo "[" > "$OUTPUT_FILE" 9 | 10 | # Find all files in the docs directory and its subdirectories 11 | # Exclude directories and format the output as JSON 12 | find "$DOCS_DIR" -type f | while read -r file; do 13 | # Replace the leading "./docs/" with "" to keep the relative path for the JSON file 14 | relative_path=${file#"$DOCS_DIR/"} 15 | 16 | # Escape double quotes and backslashes in file names for JSON format 17 | escaped_file=$(printf '%s\n' "$relative_path" | sed 's/\\/\\\\/g; s/"/\\"/g') 18 | 19 | echo "\"/$escaped_file\"," >> "$OUTPUT_FILE" 20 | done 21 | 22 | # Remove the trailing comma from the last line 23 | sed -i '$ s/,$//' "$OUTPUT_FILE" 24 | 25 | # Close the JSON array 26 | echo "]" >> "$OUTPUT_FILE" 27 | 28 | echo "File list generated: $OUTPUT_FILE" 29 | 30 | -------------------------------------------------------------------------------- /image/kaios-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/image/kaios-banner.png -------------------------------------------------------------------------------- /image/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/image/mockup.png -------------------------------------------------------------------------------- /image/mockup.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/image/mockup.xcf -------------------------------------------------------------------------------- /image/screenshoot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/strukturart/feedolin/30c1dffb36f326ae27c5df6a814e22f9f49a5d3e/image/screenshoot.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "feedolin", 3 | "version": "1.1.0", 4 | "source": "application/index.html", 5 | "browserslist": "Firefox <= 48", 6 | "scripts": { 7 | "build": "rm -rf dist/* && parcel build --no-source-maps --no-content-hash application/index.html && cp application/manifest.webapp dist && mkdir dist/assets && cp -r application/assets/icons dist/assets/ && mkdir -p dist/assets/js/ && cp application/assets/js/kaiads.v5.min.js dist/assets/js/", 8 | "run": "parcel", 9 | "web": "rm -rf docs/* && parcel build --dist-dir docs --public-url ./ --no-source-maps --no-content-hash application/index.html && cp application/manifest.webapp docs && cp application/manifest.webmanifest docs && mkdir -p docs/assets && cp -r application/assets/icons docs/assets/ && mkdir -p docs/assets/js/ && cp application/assets/js/kaiads.v5.min.js docs/assets/js/ && cp -r application/assets/fonts docs/assets/&& ./generate_file_list.sh" 10 | }, 11 | "devDependencies": { 12 | "@agragregra/parcel-namer-beautify": "^1.0.1", 13 | "@babel/cli": "^7.18.10", 14 | "@babel/core": "^7.19.1", 15 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 16 | "@babel/plugin-transform-named-capturing-groups-regex": "^7.17.12", 17 | "@babel/plugin-transform-runtime": "^7.18.5", 18 | "@babel/preset-env": "^7.19.1", 19 | "@parcel/packager-raw-url": "^2.12.0", 20 | "@parcel/resolver-default": "^2.12.0", 21 | "@parcel/transformer-html": "^2.13.3", 22 | "@parcel/transformer-webmanifest": "^2.12.0", 23 | "assert": "^2.1.0", 24 | "babel-plugin-transform-async-to-promises": "^0.8.18", 25 | "buffer": "^6.0.3", 26 | "core-js": "^3.39.0", 27 | "crypto-browserify": "^3.12.0", 28 | "events": "^3.3.0", 29 | "https-browserify": "^1.0.0", 30 | "os-browserify": "^0.3.0", 31 | "parcel": "^2.13.3", 32 | "parcel-namer-rewrite": "^2.10.3-rc.2", 33 | "path-browserify": "^1.0.1", 34 | "process": "^0.11.10", 35 | "stream-browserify": "^3.0.0", 36 | "stream-http": "^3.2.0", 37 | "string_decoder": "^1.3.0", 38 | "svgo": "^3.3.2", 39 | "timers-browserify": "^2.0.12", 40 | "url": "^0.11.4" 41 | }, 42 | "dependencies": { 43 | "@parcel/reporter-dev-server": "^2.13.3", 44 | "@parcel/service-worker": "^2.8.0", 45 | "dayjs": "^1.11.13", 46 | "dotenv": "^16.4.1", 47 | "fast-xml-parser": "^4.5.0", 48 | "localforage": "^1.10.0", 49 | "mithril": "^2.0.4", 50 | "sanitize-html": "^2.13.0", 51 | "swiped-events": "1.2.0", 52 | "url-search-params-polyfill": "^8.1.1", 53 | "uuid": "^9.0.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /proxy.php: -------------------------------------------------------------------------------- 1 | "PROXY ACCESS DENIED! URL not specified"]); 27 | exit(); 28 | } 29 | 30 | // Prepare headers 31 | $headers = []; 32 | foreach (getallheaders() as $key => $value) { 33 | if (in_array(strtolower($key), ['content-type', 'authorization', 'x-requested-with'])) { 34 | $headers[] = "$key: $value"; 35 | } 36 | } 37 | 38 | // Prepare CURL options 39 | $curl = curl_init(); 40 | switch (strtoupper($method)) { 41 | case 'POST': 42 | $postData = $isJsonRequest ? $requestData : $_POST; 43 | unset($postData['method'], $postData['cors']); 44 | curl_setopt_array($curl, [ 45 | CURLOPT_URL => $url, 46 | CURLOPT_POST => true, 47 | CURLOPT_POSTFIELDS => $isJsonRequest ? json_encode($postData) : http_build_query($postData), 48 | ]); 49 | break; 50 | 51 | case 'GET': 52 | $getData = $isJsonRequest ? $requestData : $_GET; 53 | unset($getData['method'], $getData['cors']); 54 | $queryString = http_build_query($getData); 55 | curl_setopt($curl, CURLOPT_URL, $url . ($queryString ? "?$queryString" : "")); 56 | break; 57 | 58 | default: 59 | echo json_encode(["message" => "Proxy only supports POST and GET requests"]); 60 | exit(); 61 | } 62 | 63 | curl_setopt_array($curl, [ 64 | CURLOPT_RETURNTRANSFER => true, 65 | CURLOPT_SSL_VERIFYPEER => false, // Disable for development, enable in production 66 | CURLOPT_HTTPHEADER => $headers, 67 | ]); 68 | 69 | // Execute CURL request 70 | $response = curl_exec($curl); 71 | $error = curl_error($curl); 72 | curl_close($curl); 73 | 74 | if ($error) { 75 | echo json_encode(["error" => $error]); 76 | exit(); 77 | } 78 | 79 | // Set response content type if possible 80 | if (json_decode($response) !== null) { 81 | header('Content-Type: application/json'); 82 | } elseif (strpos($response, '