Skip to content

Commit

Permalink
+ Support for importing & exporting OPML(closes #1)
Browse files Browse the repository at this point in the history
+ Support for Video & Youtube(closes #47)
+ Automatic suggestion for folder names(closes #3)
+ Filename can be defined with template(closes #36)
+ better error handling when refreshing settings file
+ CleanupModal(closes #31)
+ Support for subfolders in filtered folders(closes #35)
~ fixed a bug where $ in the content would duplicate data(closes #48)
~ switched from rollup to esbuild
~ started preparations for unit tests
  • Loading branch information
joethei committed Dec 10, 2021
1 parent 123356b commit 6415216
Show file tree
Hide file tree
Showing 37 changed files with 1,440 additions and 745 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Install modules
run: yarn

- name: Lint
run: yarn run lint
- name: Lint CSS
run: yarn run lint-css
5 changes: 5 additions & 0 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extension": ["ts"],
"spec": "src/**/*.spec.ts",
"require": "ts-node/register"
}
7 changes: 7 additions & 0 deletions .stylelintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": ["stylelint-config-recommended"],
"rules": {
"font-family-no-missing-generic-family-keyword": null,
"no-descending-specificity": null
}
}
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,31 @@ Plugin for [Obsidian](https://obsidian.md)

## Features
- Reading RSS feeds from within obsidian
- Sorting feeds into folders(sorting into nested folders is on the roadmap [#3](https://github.com/joethei/obsidian-rss/issues/3))
- Sorting feeds into folders
- staring articles
- creating new notes from articles
- pasting article into current note
- creating custom filters
- tagging articles
- support for audio and video feeds
- reading articles with Text to speech (if the [TTS plugin](https://github.com/joethei/obsidian-tts) is installed)
- multi language support(see [#43](https://github.com/joethei/obsidian-rss/issues/43) for translation instructions)
- and more on the [Roadmap](https://github.com/joethei/obsidian-rss/projects/1)

![Demo GIF](https://i.joethei.space/QQATWu36eC.gif)


## Finding the RSS feed for a website

- Search for the RSS logo or a link on the website
- Use an browser addon ([Firefox](https://addons.mozilla.org/en-US/firefox/addon/awesome-rss/), [Chrome based](https://chrome.google.com/webstore/detail/get-rss-feed-url/kfghpdldaipanmkhfpdcjglncmilendn?hl=de))
- Search the websites sourcecode for `rss`

## Tips
- get fulltext content for some truncated RSS feeds with [morss.it](https://morss.it/)
- get feeds from some social media sites with [RSS Box](https://rssbox.herokuapp.com/)
- Filter content from feeds [SiftRSS](https://siftrss.com/)

## Styling
If you want to style the plugin differently you can use the following css classes

Expand Down
34 changes: 34 additions & 0 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import esbuild from "esbuild";
import process from "process";
import builtins from 'builtin-modules';
import sveltePlugin from "esbuild-svelte";
import sveltePreprocess from "svelte-preprocess";

const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
https://github.com/joethei/obisidian-rss
*/
`;

const prod = (process.argv[2] === 'production');

esbuild.build({
banner: {
js: banner,
},
entryPoints: ['src/main.ts'],
bundle: true,
external: ['obsidian', 'electron', ...builtins],
plugins: [sveltePlugin({
preprocess: sveltePreprocess()
})],
format: 'cjs',
watch: !prod,
target: 'es2016',
logLevel: "info",
sourcemap: prod ? false : 'inline',
treeShaking: true,
outfile: 'main.js',
}).catch(() => process.exit(1));
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "rss-reader",
"name": "RSS Reader",
"version": "0.7.1",
"version": "0.8.0",
"minAppVersion": "0.9.12",
"description": "Read RSS Feeds from within obsidian",
"author": "Johannes Theiner",
Expand Down
29 changes: 19 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,44 +1,53 @@
{
"name": "rss-reader",
"version": "0.7.1",
"version": "0.8.0",
"description": "Read RSS Feeds from inside obsidian",
"main": "main.js",
"scripts": {
"dev": "rollup --config rollup.config.js -w",
"build": "rollup --config rollup.config.js --environment BUILD:production",
"lint": "eslint . --ext .ts"
"dev": "node esbuild.config.mjs",
"build": "node esbuild.config.mjs production",
"lint": "eslint . --ext .ts",
"lint-css": "stylelint styles.css",
"test": "mocha"
},
"keywords": [],
"author": "Johannes Theiner",
"license": "GPL-3.0",
"devDependencies": {
"@popperjs/core": "^2.10.2",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-typescript": "^8.2.1",
"@types/chai": "^4.3.0",
"@types/lodash.groupby": "^4.6.6",
"@types/lodash.keyby": "^4.6.6",
"@types/lodash.mergewith": "^4.6.6",
"@types/lodash.sortby": "^4.7.6",
"@types/lodash.values": "^4.3.6",
"@types/mocha": "^9.0.0",
"@types/node": "^14.14.37",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"chai": "^4.3.4",
"esbuild": "^0.14.2",
"esbuild-svelte": "^0.6.0",
"eslint": "^7.32.0",
"jsdom": "^19.0.0",
"jsdom-global": "^3.0.2",
"lodash.groupby": "^4.6.0",
"lodash.keyby": "^4.6.0",
"lodash.mergewith": "^4.6.2",
"lodash.sortby": "^4.7.0",
"lodash.values": "^4.3.0",
"mocha": "^9.1.3",
"obsidian": "^0.12.0",
"obsidian-community-lib": "1.2.0",
"rollup": "^2.32.1",
"rollup-plugin-svelte": "^7.1.0",
"obsimian": "^0.4.0",
"stylelint": "^14.1.0",
"stylelint-config-standard": "^24.0.0",
"svelte": "^3.43.1",
"svelte-preprocess": "^4.9.8",
"ts-md5": "^1.2.10",
"ts-node": "^10.4.0",
"tslib": "^2.2.0",
"tslint": "^6.1.3",
"typescript": "^4.2.4"
}
}
42 changes: 0 additions & 42 deletions rollup.config.js

This file was deleted.

58 changes: 31 additions & 27 deletions src/functions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {RssFeedItem} from "./parser/rssParser";
import {htmlToMarkdown, MarkdownView, normalizePath, Notice, TextComponent} from "obsidian";
import {htmlToMarkdown, MarkdownView, normalizePath, Notice, TextComponent, moment} from "obsidian";
import {TextInputPrompt} from "./modals/TextInputPrompt";
import {FILE_NAME_REGEX} from "./consts";
import {isInVault} from "obsidian-community-lib";
Expand All @@ -14,11 +14,13 @@ export async function createNewNote(plugin: RssReaderPlugin, item: RssFeedItem)
if(plugin.settings.saveLocation === "custom") {
dir = plugin.settings.saveLocationFolder;
}

let filename = applyTemplate(item, plugin.settings.defaultFilename, plugin.settings);
//make sure there are no slashes in the title.
const title = item.title.replace(/[\/\\:]/g, ' ');
filename = filename.replace(/[\/\\:]/g, ' ');

const inputPrompt = new TextInputPrompt(plugin.app, t("specify_name"), t("cannot_contain") + " * \" \\ / < > : | ?", title, title);
if(plugin.settings.askForFilename) {
const inputPrompt = new TextInputPrompt(plugin.app, t("specify_name"), t("cannot_contain") + " * \" \\ / < > : | ?", filename, filename);
await inputPrompt
.openAndGetValue(async (text: TextComponent) => {
const value = text.getValue();
Expand All @@ -36,7 +38,7 @@ export async function createNewNote(plugin: RssReaderPlugin, item: RssFeedItem)
await createNewFile(plugin, item, filePath, value);
});
}else {
const replacedTitle = item.title.replace(FILE_NAME_REGEX, '');
const replacedTitle = filename.replace(FILE_NAME_REGEX, '');
const filePath = normalizePath([dir, `${replacedTitle}.md`].join('/'));
await createNewFile(plugin, item, filePath, item.title);
}
Expand Down Expand Up @@ -92,16 +94,15 @@ export async function pasteToNote(plugin: RssReaderPlugin, item: RssFeedItem) :
}

function applyTemplate(item: RssFeedItem, template: string, settings: RssReaderSettings, filename?: string) : string {
const content = htmlToMarkdown(item.content);

let result = replaceAll(template, "{{title}}", item.title);
result = replaceAll(result, "{{link}}", item.link);
result = replaceAll(result, "{{author}}", item.creator);
result = replaceAll(result, "{{content}}", content);
result = replaceAll(result, "{{published}}", window.moment(item.pubDate).format(settings.dateFormat));
result = replaceAll(result, "{{feed}}", item.feed);
result = replaceAll(result, "{{folder}}", item.folder);
result = replaceAll(result, "{{description}}", item.description);
let result = template.replace(/{{title}}/g, item.title);
result = result.replace(/{{link}}/g, item.link);
result = result.replace(/{{author}}/g, item.creator);
result = result.replace(/{{published}}/g, moment(item.pubDate).format(settings.dateFormat));
result = result.replace(/{{date}}/g, moment().format(settings.dateFormat));
result = result.replace(/{{feed}}/g, item.feed);
result = result.replace(/{{folder}}/g, item.folder);
result = result.replace(/{{description}}/g, item.description);
result = result.replace(/{{media}}/g, item.enclosure);

result = result.replace(/({{tags:).*(}})/g, function (k) {
const value = k.split(":")[1];
Expand All @@ -115,13 +116,25 @@ function applyTemplate(item: RssFeedItem, template: string, settings: RssReaderS
return item.tags.map(i => '#' + i).join(separator);
});

result = replaceAll(result, "{{tags}}", item.tags.join(", "));
result = replaceAll(result, "{{#tags}}", item.tags.map(i => '#' + i).join(", "));
result = result.replace(/{{tags}}/, item.tags.join(", "));
result = result.replace(/{{#tags}}/, item.tags.map(i => '#' + i).join(", "));
if(filename) {
result = replaceAll(result, "{{filename}}", filename);
result = replaceAll(result, "{{created}}", window.moment().format(settings.dateFormat));
result = result.replace(/{{filename}}/g, filename);
result = result.replace(/{{created}}/g, moment().format(settings.dateFormat));
}

let content = htmlToMarkdown(item.content);

/*
fixes #48
replacing $ with $$$, because that is a special regex character:
https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/string/replace#specifying_a_string_as_a_parameter
solution taken from: https://stackoverflow.com/a/22612228/5589264
*/
content = content.replace(/\$/g, "$$$");

result = result.replace(/{{content}}/g, content);

return result;
}

Expand All @@ -130,12 +143,3 @@ export function openInBrowser(item: RssFeedItem) : void {
window.open(item.link, '_blank');
}
}

//taken from: https://stackoverflow.com/a/1144788/5589264
function escapeRegExp(string: string) : string {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}

function replaceAll(str: string, find: string, replace: string) : string {
return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
}
22 changes: 22 additions & 0 deletions src/l10n/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## Localization

The plugin has full localization support, and will attempt to load the current Obsidian locale. If one does not exist, it will fall back to English.

### Adding a new Locale

New locales can be added by creating a pull request. Two things should be done in this pull request:

1. Create the locale in the `locales` folder by copying the `en.ts` file. This file should be given a name matching the [ISO 639-1](https://www.loc.gov/standards/iso639-2/php/English_list.php) code for that language.
2. Create the translation by editing the value of each property.
3. Add the import in `locales.ts`.
4. Add the language to the `localeMap` variable.

#### Wildcards

Some strings in the locale have wildcards in them, such as `%1`. This is used by the plugin to insert dynamic data into the translated string.

For example:

`Loading RSS Reader v%1`: The plugin will insert the version number for `%1`.

This allows control of plural syntaxes or placement of file names in the sentence.
36 changes: 36 additions & 0 deletions src/l10n/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export default {
removed_from_favorites: "Von den Favoriten entfernt",
added_to_favorites: "Als Favorit markiert",

read: "gelesen",
unread: "ungelesen",
favorites: "Favoriten",
favorite: "Favorit",
tags: "Tags",
tag: "Tag",

//base modal
save: "Speichern",
cancel: "Abbrechen",
Expand Down Expand Up @@ -124,4 +131,33 @@ export default {
filter_exists: "Es exisitiert bereits ein Feed mit diesem Namen",
hotkeys: "Tastenkürzel",
hotkeys_reading: "in der Leseansicht",

refreshed_feeds: "Feeds aktualisiert",

//import modal
import: "Importieren",
import_opml: "Aus OPML importieren",
imported_x_feeds: "%1 Feeds importiert",
choose_file: "Datei auswählen",
choose_file_help: "Wähle eine Datei aus der importiert werden soll",
export_opml: "Als OPML exportieren",

default_filename: "Vorlage für Dateinamen",
default_filename_help: "Alle Variablen aus der einfügen Vorlage können verwendet werden",

//cleanup modal
cleanup: "Artikel aufräumen",
cleanup_help: "Entfernt alle Artikel auf die folgende Kriterien zutreffen",
cleanup_help2: "Alle Artikel die noch im Feed vorhanden sind werden beim nächsten aktualisieren wieder erscheinen",
perform_cleanup: "ausführen",
all: "Alle",
from_feed: "von Feed",
older_than: "älter als X Tage",
older_than_help: "Leerlassen für alle, wird ignoriert wenn Artikel kein Veröffentlichungsdatum hat",
advanced: "Erweitert",
remove_wrong_feed: "Alle Artikel entfernen die im falschen Feed gelandet sind",
remove_wrong_feed_help: "Aufgrund eines Fehlers in Versionen vor 0.8 könnte dies passiert sein",
scanning_items: "Verarbeite Artikel (%1 / %2)",

created_export: "OPML Export in Vault erstellt"
}
Loading

0 comments on commit 6415216

Please sign in to comment.