[READ-ONLY] Mirror of https://github.com/probablykasper/LaRSS. RSS Generator for iTunes larss.kasper.space
rss website
0

Configure Feed

Select the types of activity you want to include in your feed.

Gist creation and loading

+187 -32
+4
docs/index.css
··· 10 10 background: linear-gradient(to bottom, #394290, #22244A); 11 11 } 12 12 13 + body .container-sample { 14 + display: none; 15 + } 16 + 13 17 body .container { 14 18 display: flex; 15 19 flex-direction: column;
+13 -12
docs/index.html
··· 20 20 <input tabindex="-1" placeholder="GitHub Personal Access Token" class="pat"/> 21 21 <h2>How to get your personal access token</h2> 22 22 <p>1. Log in or register on <a target="_blank" href="https://github.com/">GitHub</a>.</p> 23 - <p>2. Create a new "Personal access token" by going to <a target="_blank" href="https://github.com/settings/tokens/new">this link</a>. Write Taskler or whatever into the Token description field. Make sure "gist" is checked, and click "Generate token".</p> 23 + <p>2. Create a new "Personal access token" by going to <a target="_blank" href="https://github.com/settings/tokens/new">this link</a>. Write LaRSS into the Token description field. Make sure "gist" is checked, and click "Generate token".</p> 24 24 <p>3. Copy the random text shown in the green field (This is your personal access token). Paste this text into the field above, in every instance of Taskler you have.</p> 25 25 <p><b>If your personal access token has already been used, your offline tasks will be replaced.</b></p> 26 26 <button tabindex="-1" class="save-pat">SAVE</button> 27 27 </div> 28 28 </div> 29 - <div class="container"><img src="logo.png" class="logo"/> 29 + <div class="container"></div> 30 + <div class="container-sample"><img src="logo.png" class="logo"/> 30 31 <div class="general-info"> 31 32 <div class="header"> 32 33 <div class="collapse-icon"></div> ··· 42 43 <input type="text" placeholder="Subtitle" class="subtitle"/> 43 44 </div> 44 45 <div class="row"> 45 - <textarea type="text" placeholder="Description" rows="1" class="description"></textarea> 46 + <textarea placeholder="Description" rows="1" class="description"></textarea> 46 47 </div> 47 48 <div class="row"> 48 49 <input type="text" placeholder="URL" class="url"/> 49 - <input type="text" placeholder="Image URL" class="image-url"/> 50 + <input type="text" placeholder="Image URL" class="imageURL"/> 50 51 </div> 51 52 <div class="row"> 52 53 <select class="language"> ··· 56 57 <option value="Music">Music</option> 57 58 </select> 58 59 <select class="explicit"> 59 - <option value="Music">Explicit</option> 60 - <option value="Music">Not explicit</option> 60 + <option value="yes">Explicit</option> 61 + <option value="no">Not explicit</option> 61 62 </select> 62 63 <input type="text" placeholder="Copyright" class="copyright"/> 63 64 </div> 64 65 <div class="row"> 65 - <input type="text" placeholder="Owner Name" class="ownerName"/> 66 - <input type="text" placeholder="Owner Email" class="ownerEmail"/> 66 + <input type="text" placeholder="Owner Name" class="name"/> 67 + <input type="text" placeholder="Owner Email" class="email"/> 67 68 </div> 68 69 </div> 69 70 </div> ··· 78 79 <input type="text" placeholder="Title" class="title"/> 79 80 </div> 80 81 <div class="row"> 81 - <input type="text" placeholder="Audio URL" class="audio-url"/> 82 - <input type="text" placeholder="Image URL" class="image-url"/> 82 + <input type="text" placeholder="Audio URL" class="audioURL"/> 83 + <input type="text" placeholder="Image URL" class="imageURL"/> 83 84 </div> 84 85 <div class="row"> 85 86 <select class="explicit"> 86 - <option value="Music">Explicit</option> 87 - <option value="Music">Not explicit</option> 87 + <option value="yes">Explicit</option> 88 + <option value="no">Not explicit</option> 88 89 </select> 89 90 <input type="text" placeholder="Date" class="date"/> 90 91 <input type="text" placeholder="Duration" class="duration"/>
+156 -9
docs/index.js
··· 69 69 })(); 70 70 71 71 var pat = localStorage.getItem("pat"); 72 + var gistId = localStorage.getItem("gistId"); 72 73 function getFilename(pat) { 73 74 return { 74 75 json: "larss-"+pat.slice(0,4)+".json", ··· 76 77 }; 77 78 } 78 79 79 - function loadFeed(callback) { 80 + load(pat); 81 + function load(pat, callback) { 80 82 if (pat) { 81 - 83 + // if gistId is already saved, load it. Otherwise, find the gistId 84 + if (gistId) { 85 + findGist(pat, gistId, function(content) { 86 + // if the gist was found, load it. Otherwise 87 + if (content) { 88 + gistFound(content); 89 + if (callback) callback(); 90 + } else { 91 + createGist(pat, function(id, content) { 92 + localStorage.setItem("gistId", id); 93 + gistId = id; 94 + gistFound(content); 95 + if (callback) callback(); 96 + }); 97 + if (callback) callback(); 98 + } 99 + }); 100 + } else { 101 + findGistId(pat, function(id) { 102 + if (id) { 103 + findGist(pat, id, function(content) { 104 + gistFound(content); 105 + if (callback) callback(); 106 + resizeTextareas(); 107 + }); 108 + } else { 109 + createGist(pat, function(id, content) { 110 + localStorage.setItem("gistId", id); 111 + gistId = id; 112 + gistFound(content); 113 + if (callback) callback(); 114 + }); 115 + } 116 + }); 117 + } 118 + } 119 + } 120 + function gistFound(json, updatedAt) { 121 + var container = document.querySelector(".container"); 122 + container.innerHTML = '<img src="logo.png" class="logo">'; 123 + var generalInfo = document.querySelector(".container-sample .general-info").cloneNode(true); 124 + var gio = json.generalInfo; // generalInfoObject 125 + var io = json.items; // generalInfoObject 126 + container.appendChild(generalInfo); 127 + generalInfo.querySelector("input.title").value = gio.title; 128 + generalInfo.querySelector("input.author").value = gio.author; 129 + generalInfo.querySelector("input.subtitle").value = gio.subtitle; 130 + generalInfo.querySelector("textarea.description").value = gio.description; 131 + generalInfo.querySelector("input.url").value = gio.url; 132 + generalInfo.querySelector("input.imageURL").value = gio.imageURL; 133 + generalInfo.querySelector('select.language').value = gio.language; 134 + generalInfo.querySelector('select.category').value = gio.category; 135 + generalInfo.querySelector('select.explicit').value = gio.explicit; 136 + generalInfo.querySelector("input.copyright").value = gio.copyright; 137 + generalInfo.querySelector("input.name").value = gio.name; 138 + generalInfo.querySelector("input.email").value = gio.email; 139 + for (var i = 0; i < json.items.length; i++) { 140 + var itemSample = document.querySelector(".container-sample .item").cloneNode(true); 141 + container.appendChild(itemSample); 142 + itemSample.querySelector("input.guid").value = io[i].guid; 143 + itemSample.querySelector("input.title").value = io[i].title; 144 + itemSample.querySelector("input.audioURL").value = io[i].audioURL; 145 + itemSample.querySelector("input.imageURL").value = io[i].imageURL; 146 + itemSample.querySelector('select.explicit').value = io[i].explicit; 147 + itemSample.querySelector("input.date").value = io[i].date; 148 + itemSample.querySelector("input.duration").value = io[i].duration; 149 + itemSample.querySelector("textarea.summary").value = io[i].summary; 82 150 } 151 + resizeTextareas(); 83 152 } 84 153 85 154 (function patDialog() { ··· 94 163 saveButton.addEventListener("click", function() { 95 164 console.log("saving access token"); 96 165 // save personal access token 97 - pat = pat.value; 166 + pat = patInput.value; 98 167 localStorage.setItem("pat", pat); 99 168 jsonGistId = null; 100 169 rssGistId = null; 101 - gistDate = null; 102 170 localStorage.removeItem("gistId"); 103 - localStorage.removeItem("gistDate"); 104 171 dialog.classList.add("saving"); 105 - reloadTasks(function() { 172 + load(pat, function() { 106 173 dialog.classList.remove("saving"); 107 174 dialog.classList.remove("visible"); 108 175 }); ··· 126 193 console.log(gist); 127 194 var jsonFile = gist.files[getFilename(pat).json]; 128 195 var rssFile = gist.files[getFilename(pat).rss]; 129 - callback(JSON.parse(jsonFile.content), JSON.parse(rssFile.content), new Date(gist.updated_at)); 196 + callback(JSON.parse(jsonFile.content), new Date(gist.updated_at)); 130 197 }, 131 198 onError: function(res, code) { 132 199 res = JSON.parse(res); 133 200 console.log("----------------------------------- findGist err"); 134 201 console.log(code); 135 202 console.log(res); 203 + callback(false); 136 204 } 137 205 }); 138 206 } 139 207 208 + function generateXML(json) { 209 + var xml = ''; 210 + function add(index, tag) { 211 + if (tag.startsWith("itunes:")) var prop = tag.slice(7); 212 + else var prop = tag; 213 + if (index == "generalInfo") { 214 + var value = json.generalInfo[prop]; 215 + } else { 216 + var value = json.items[index][prop]; 217 + } 218 + xml += '<'+tag+'>'+value+'</'+tag+'>'; 219 + } 220 + xml += '<?xml version="1.0" encoding="UTF-8"?>'; 221 + xml += '<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">'; 222 + xml += '<channel>'; 223 + add("generalInfo", "title"); 224 + add("generalInfo", "itunes:author"); 225 + add("generalInfo", "itunes:subtitle"); 226 + add("generalInfo", "description"); 227 + add("generalInfo", "link"); 228 + xml += '<itunes:image href="'+json.generalInfo.imageURL+'"/>'; 229 + add("generalInfo", "language"); 230 + xml += '<itunes:category text="'+json.generalInfo.category+'"/>'; 231 + add("generalInfo", "itunes:explicit"); 232 + add("generalInfo", "copyright"); 233 + xml += '<itunes:owner>'; 234 + add("generalInfo", "itunes:name"); 235 + add("generalInfo", "itunes:email"); 236 + xml += '</itunes:owner>'; 237 + 238 + for (var i = 0; i > json.items.length; i++) { 239 + xml += '<item>'; 240 + add(i, "guid"); 241 + add(i, "title"); 242 + xml += '<enclosure type="audio/mpeg" url="'+json.items[i].audioURL+'" length="0"/>'; 243 + xml += '<itunes:image href="'+json.items[i].imageURL+'"/>'; 244 + add(i, "itunes:explicit"); 245 + add(i, "pubDate"); 246 + add(i, "itunes:duration"); 247 + add(i, "itunes:summary"); 248 + xml += '</item>'; 249 + } 250 + xml += '</channel>'; 251 + xml += '</rss>'; 252 + return xml; 253 + } 254 + 140 255 function createGist(pat, callback) { 141 256 var req = { 142 257 description: "RSS Gist created by LaRSS", 143 258 files: {} 144 259 }; 260 + var defaultJSON = { 261 + generalInfo: { 262 + title: "", 263 + author: "", 264 + subtitle: "", 265 + description: "", 266 + url: "", 267 + imageURL: "", 268 + explicit: "no", 269 + language: "en-us", 270 + category: "Music", 271 + copyright: "", 272 + name: "", 273 + email: "" 274 + }, 275 + items: [ 276 + { 277 + guid: "", 278 + title: "", 279 + audioURL: "", 280 + imageURL: "", 281 + explicit: "no", 282 + date: "Wed, 25 Oct 2017 22:00:00 CET", 283 + duration: "", 284 + summary: "" 285 + } 286 + ] 287 + }; 145 288 req.files[getFilename(pat).json] = { 146 - content: JSON.stringify(json) 289 + content: JSON.stringify(defaultJSON) 290 + }; 291 + req.files[getFilename(pat).rss] = { 292 + content: "empty rn" 147 293 }; 148 294 xhr({ 149 295 url: "https://api.github.com/gists", ··· 154 300 res = JSON.parse(res); 155 301 console.log("----------------------------------- createGist suc"); 156 302 console.log(res); 157 - callback(res.id, new Date(res.updated_at)); 303 + callback(res.id, defaultJSON, new Date(res.updated_at)); 158 304 }, 159 305 onError: function(res, code) { 160 306 res = JSON.parse(res); ··· 188 334 console.log("----------------------------------- findgistid err"); 189 335 console.log(code); 190 336 console.log(res); 337 + callback(false); 191 338 } 192 339 }); 193 340 }
+12 -11
index.pug
··· 19 19 input.pat(tabindex="-1", placeholder="GitHub Personal Access Token") 20 20 h2 How to get your personal access token 21 21 p 1. Log in or register on <a target="_blank" href="https://github.com/">GitHub</a>. 22 - p 2. Create a new "Personal access token" by going to <a target="_blank" href="https://github.com/settings/tokens/new">this link</a>. Write Taskler or whatever into the Token description field. Make sure "gist" is checked, and click "Generate token". 22 + p 2. Create a new "Personal access token" by going to <a target="_blank" href="https://github.com/settings/tokens/new">this link</a>. Write LaRSS into the Token description field. Make sure "gist" is checked, and click "Generate token". 23 23 p 3. Copy the random text shown in the green field (This is your personal access token). Paste this text into the field above, in every instance of Taskler you have. 24 24 p <b>If your personal access token has already been used, your offline tasks will be replaced.</b> 25 25 button(tabindex="-1").save-pat SAVE 26 26 .container 27 + .container-sample 27 28 img.logo(src="logo.png") 28 29 .general-info 29 30 .header ··· 37 38 .row 38 39 input(type="text" placeholder="Subtitle").subtitle 39 40 .row 40 - textarea(type="text" placeholder="Description" rows="1").description 41 + textarea(placeholder="Description" rows="1").description 41 42 .row 42 43 input(type="text" placeholder="URL").url 43 - input(type="text" placeholder="Image URL").image-url 44 + input(type="text" placeholder="Image URL").imageURL 44 45 .row 45 46 select.language 46 47 option(value="en-us") en-us 47 48 select.category 48 49 option(value="Music") Music 49 50 select.explicit 50 - option(value="Music") Explicit 51 - option(value="Music") Not explicit 51 + option(value="yes") Explicit 52 + option(value="no") Not explicit 52 53 input(type="text" placeholder="Copyright").copyright 53 54 .row 54 - input(type="text" placeholder="Owner Name").ownerName 55 - input(type="text" placeholder="Owner Email").ownerEmail 55 + input(type="text" placeholder="Owner Name").name 56 + input(type="text" placeholder="Owner Email").email 56 57 .item 57 58 .header 58 59 .collapse-icon ··· 62 63 input(type="text" placeholder="GUID").guid 63 64 input(type="text" placeholder="Title").title 64 65 .row 65 - input(type="text" placeholder="Audio URL").audio-url 66 - input(type="text" placeholder="Image URL").image-url 66 + input(type="text" placeholder="Audio URL").audioURL 67 + input(type="text" placeholder="Image URL").imageURL 67 68 .row 68 69 select.explicit 69 - option(value="Music") Explicit 70 - option(value="Music") Not explicit 70 + option(value="yes") Explicit 71 + option(value="no") Not explicit 71 72 input(type="text" placeholder="Date").date 72 73 input(type="text" placeholder="Duration").duration 73 74 .row
+2
index.sass
··· 8 8 body 9 9 margin: 0px 10 10 background: linear-gradient(to bottom, #394290, #22244A) 11 + .container-sample 12 + display: none 11 13 .container 12 14 display: flex 13 15 flex-direction: column