diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b014aec
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,211 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+=======================================================================
+
+Component licenses for mycroft-core:
+
+The mycroft-core software references various Python Packages (via PIP),
+each of which has a separate license. All are compatible with the
+Apache 2.0 license. See the referenced packages listed in the
+"requirements/requirements.txt" file for specific terms and conditions.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cf23148
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+#
News Streams
+
+News Streams catalog
+
+## About
+
+News streams from around the globe
+
+
+
+
+
+PRs adding new feeds welcome, especially for unsupported languages
+
+## Examples
+
+* "play the news"
+* "play npr news"
+* "play euronews"
+* "play catalan news"
+* "play portuguese news"
+* "play news in spanish"
+
+## Credits
+- JarbasAl
+
+## Category
+**Information**
+
+## Tags
+#news
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..40ce8e4
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,715 @@
+from datetime import timedelta
+import feedparser
+import re
+from os.path import join, dirname
+from pytz import timezone
+from mycroft.util.time import now_local
+from mycroft.util.parse import match_one
+from ovos_utils.skills.templates.common_play import BetterCommonPlaySkill
+from ovos_utils.playback import CPSMatchType, CPSPlayback, CPSMatchConfidence
+import requests
+from youtube_searcher import extract_videos
+
+
+# news uri extractors
+def tsf():
+ """Custom inews fetcher for TSF news."""
+ feed = ('https://www.tsf.pt/stream/audio/{year}/{month:02d}/'
+ 'noticias/{day:02d}/not{hour:02d}.mp3')
+ uri = None
+ i = 0
+ status = 404
+ date = now_local(timezone('Portugal'))
+ while status != 200 and i < 6:
+ uri = feed.format(hour=date.hour, year=date.year,
+ month=date.month, day=date.day)
+ status = requests.get(uri).status_code
+ date -= timedelta(hours=1)
+ i += 1
+ if status != 200:
+ return None
+ return uri
+
+
+def gpb():
+ """Custom news fetcher for GBP news."""
+ feed = 'http://feeds.feedburner.com/gpbnews/GeorgiaRSS?format=xml'
+ data = feedparser.parse(feed)
+ next_link = None
+ for entry in data['entries']:
+ # Find the first mp3 link with "GPB {time} Headlines" in title
+ if 'GPB' in entry['title'] and 'Headlines' in entry['title']:
+ next_link = entry['links'][0]['href']
+ break
+ html = requests.get(next_link)
+ # Find the first mp3 link
+ # Note that the latest mp3 may not be news,
+ # but could be an interview, etc.
+ mp3_find = re.search(r'href="(?P.+\.mp3)"'.encode(), html.content)
+ if mp3_find is None:
+ return None
+ url = mp3_find.group('mp3').decode('utf-8')
+ return url
+
+
+def abc():
+ """Custom news fetcher for ABC News Australia briefing"""
+ date = now_local(timezone('Australia/Sydney'))
+ hour = date.strftime('%H')
+ day = date.strftime('%d')
+ month = date.strftime('%m')
+ year = date.strftime('%Y')
+ url = f"https://abcmedia.akamaized.net/news/audio/news-briefings/top" \
+ f"-stories/{year}{month}/NAUs_{hour}00flash_{day}{month}_nola.mp3"
+ # If this hours news is unavailable try the hour before
+ response = requests.get(url)
+ if response.status_code != 200:
+ hour = str(int(hour) - 1)
+ url = f"https://abcmedia.akamaized.net/news/audio/news-briefings/top" \
+ f"-stories/{year}{month}/NAUs_{hour}00flash_{day}{month}_nola.mp3"
+
+ return url
+
+
+def npr():
+ url = "https://www.npr.org/rss/podcast.php?id=500005"
+ feed = extract_rss(url)
+ return feed.split("?")[0]
+
+
+def extract_rss(feed_url):
+ try:
+ # parse RSS or XML feed
+ data = feedparser.parse(feed_url.strip())
+ # After the intro, find and start the news uri
+ # select the first link to an audio file
+
+ for link in data['entries'][0]['links']:
+ if 'audio' in link['type']:
+ # TODO return duration for proper display in UI
+ duration = link.get('length')
+ return link['href']
+ except Exception as e:
+ pass
+
+
+def extract_yt_channel(url):
+ try:
+ for e in extract_videos(url):
+ if not e["is_live"]:
+ continue
+ return e["url"]
+ except:
+ pass
+
+
+# Unified News Skill
+class NewsSkill(BetterCommonPlaySkill):
+ # default feeds per language (optional)
+ langdefaults = {
+ "pt-pt": "TSF",
+ "ca": "CCMA",
+ "es": "RNE",
+ "en-gb": "BBC",
+ "en-us": "NPR"
+ }
+ # all news streams for better-cps
+ lang2news = {
+ "en-us": {
+ "SkyStream": {
+ "aliases": ["skyuri", "sky uri", "sky news", "skynews"],
+ "uri": "https://skynews2-plutolive-vo.akamaized.net/cdhlsskynewsamericas/1013/latest.m3u8?serverSideAds=true",
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "skystream.png"),
+ "secondary_langs": ["en"]
+ },
+ "SkyStream (audio)": {
+ # names used for matching
+ "aliases": ["skyuri", "sky uri", "sky news", "skynews"],
+ # uri_extractor method or text if static
+ "uri": "https://skynews2-plutolive-vo.akamaized.net/cdhlsskynewsamericas/1013/latest.m3u8?serverSideAds=true",
+ # media types
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ # playback types
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "skystream.png"),
+ "secondary_langs": ["en"]
+ },
+ "TWC": {
+ "aliases": ["twc", "weather channel", "the weather channel"],
+ "uri": "https://weather-lh.akamaihd.net/i/twc_1@92006/master.m3u8",
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "twc.png"),
+ "secondary_langs": ["en"]
+ },
+ "TWC (audio)": {
+ "aliases": ["twc", "weather channel", "the weather channel"],
+ "uri": "https://weather-lh.akamaihd.net/i/twc_1@92006/master.m3u8",
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "twc.png"),
+ "secondary_langs": ["en"]
+ },
+ "GPB": {
+ "aliases": ["Georgia Public Broadcasting", "GPB",
+ "Georgia Public Radio"],
+ "uri": gpb,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "gpb.png"),
+ "secondary_langs": ["en"]
+ },
+ "AP": {
+ "aliases": ["AP Hourly Radio News", "Associated Press",
+ "Associated Press News",
+ "Associated Press Radio News",
+ "Associated Press Hourly Radio News"],
+ "rss_feed": "https://www.spreaker.com/show/1401466/episodes/feed",
+ "uri": extract_rss,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "AP.png"),
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "secondary_langs": ["en"]
+ },
+ "FOX": {
+ "aliases": ["FOX News", "FOX", "Fox News Channel"],
+ "rss_feed": "http://feeds.foxnewsradio.com/FoxNewsRadio",
+ "uri": extract_rss,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "FOX.png"),
+ "secondary_langs": ["en"]
+ },
+ "NPR": {
+ "aliases": ["NPR News", "NPR", "National Public Radio",
+ "National Public Radio News", "NPR News Now"],
+ "uri": npr,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "NPR.png"),
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "secondary_langs": ["en"]
+ },
+ "PBS": {
+ "aliases": ["PBS News", "PBS", "PBS NewsHour", "PBS News Hour",
+ "National Public Broadcasting Service",
+ "Public Broadcasting Service News"],
+ "rss_feed": "https://www.pbs.org/newshour/feeds/rss/podcasts/show",
+ "uri": extract_rss,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "PBS.png"),
+ "secondary_langs": ["en"]
+ },
+ },
+ "en-gb": {
+ "BBC": {
+ "aliases": ["British Broadcasting Corporation", "BBC",
+ "BBC News"],
+ "rss_feed": "https://podcasts.files.bbci.co.uk/p02nq0gn.rss",
+ "uri": extract_rss,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "BBC.png"),
+ "secondary_langs": ["en"]
+ },
+ "EuroNews": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/Euronews",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png"),
+ "secondary_langs": ["en"]
+ },
+ "EuroNews (audio)": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronews",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ "playback": CPSPlayback.AUDIO,
+ "secondary_langs": ["en"],
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ }
+ },
+ "en-au": {
+ "ABC": {
+ "aliases": ["ABC News Australia", "ABC News", "ABC"],
+ "uri": abc,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "ABC.png"),
+ "secondary_langs": ["en"]
+ }
+ },
+ "en-ca": {
+ "CBC": {
+ "aliases": ["Canadian Broadcasting Corporation", "CBC",
+ "CBC News"],
+ "uri": extract_rss,
+ "rss_feed": "https://www.cbc.ca/podcasting/includes/hourlynews.xml",
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "media_type": CPSMatchType.NEWS,
+ "image": join(dirname(__file__), "ui", "images", "CBC.png"),
+ "secondary_langs": ["en"]
+ }
+ },
+ "pt-pt": {
+ "TSF": {
+ "aliases": ["TSF", "TSF Rádio Notícias", "TSF Notícias"],
+ "uri": tsf,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "tsf.png"),
+ "secondary_langs": ["pt"]
+ },
+ "RDP-AFRICA": {
+ "aliases": ["RDP", "RDP Africa", "Noticiários RDP África"],
+ "uri": "http://www.rtp.pt//play/itunes/5442",
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "rdp_africa.png"),
+ "secondary_langs": ["pt"]
+ },
+ "EuroNews PT": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewspt",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png"),
+ "secondary_langs": ["pt"]
+ },
+ "EuroNews PT (audio)": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewspt",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ }
+ },
+ "de": {
+ "OE3": {
+ "aliases": ["OE3", "Ö3 Nachrichten"],
+ "uri": "https://oe3meta.orf.at/oe3mdata/StaticAudio/Nachrichten.mp3",
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "oe3.jpeg"),
+ "playback": CPSPlayback.AUDIO
+ },
+ "DLF": {
+ "aliases": ["DLF", "deutschlandfunk"],
+ "rss_feed": "https://www.deutschlandfunk.de/podcast-nachrichten.1257.de.podcast.xml",
+ "uri": extract_rss,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "DLF.png"),
+ "playback": CPSPlayback.AUDIO
+ },
+ "WDR": {
+ "aliases": ["WDR"],
+ "uri": "https://www1.wdr.de/mediathek/audio/wdr-aktuell-news/wdr-aktuell-152.podcast",
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "WDR.png"),
+ "playback": CPSPlayback.AUDIO
+ },
+ "EuroNews DE": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewsde",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ },
+ "EuroNews DE (audio)": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewsde",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ }
+ },
+ "nl": {
+ "VRT": {
+ "aliases": ["VRT Nieuws", "VRT"],
+ "uri": "https://progressive-audio.lwc.vrtcdn.be/content/fixed/11_11niws-snip_hi.mp3",
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "vrt.png"),
+ "playback": CPSPlayback.AUDIO
+ }
+ },
+ "sv": {
+ "Ekot": {
+ "aliases": ["Ekot"],
+ "rss_feed": "https://api.sr.se/api/rss/pod/3795",
+ "uri": extract_rss,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "Ekot.png"),
+ "playback": CPSPlayback.AUDIO
+ }
+ },
+ "es": {
+ "RNE": {
+ "aliases": ["RNE", "National Spanish Radio",
+ "Radio Nacional de España"],
+ "uri": extract_rss,
+ "media_type": CPSMatchType.NEWS,
+ "rss_feed": "http://api.rtve.es/api/programas/36019/audios.rs",
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "rne.png"),
+ "playback": CPSPlayback.AUDIO
+ },
+ "EuroNews ES": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewses",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ },
+ "EuroNews ES (audio)": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewses",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ }
+ },
+ "ca": {
+ "CCMA": {
+ "aliases": ["CCMA", "Catalunya Informació"],
+ "uri": "https://de1.api.radio-browser.info/pls/url/69bc7084-523c-11ea-be63-52543be04c81",
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "CCMA.png"),
+ "secondary_langs": ["es"]
+ }
+ },
+ "fi": {
+ "YLE": {
+ "aliases": ["YLE", "YLE News Radio"],
+ "rss_feed": "https://feeds.yle.fi/areena/v1/series/1-1440981.rss",
+ "uri": extract_rss,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS, CPSMatchType.RADIO],
+ "image": join(dirname(__file__), "ui", "images", "Yle.png"),
+ "playback": CPSPlayback.AUDIO
+ }
+ },
+ "ru": {
+ "EuroNews RU": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewsru",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ },
+ "EuroNews RU (audio)": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewsru",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ }
+ },
+ "it": {
+ "EuroNews IT": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewsit",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ },
+ "EuroNews IT (audio)": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewsit",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ }
+ },
+ "fr": {
+ "EuroNews FR": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewsft",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.VIDEO,
+ CPSMatchType.TV, CPSMatchType.NEWS],
+ "playback": CPSPlayback.GUI,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ },
+ "EuroNews FR (audio)": {
+ "aliases": ["euro", "euronews", "Euro News", "european",
+ "european news"],
+ "youtube_channel": "https://www.youtube.com/user/euronewsft",
+ "uri": extract_yt_channel,
+ "media_type": CPSMatchType.NEWS,
+ "match_types": [CPSMatchType.GENERIC, CPSMatchType.AUDIO,
+ CPSMatchType.NEWS],
+ "playback": CPSPlayback.AUDIO,
+ "image": join(dirname(__file__), "ui", "images", "euronews.png")
+ }
+ }
+ }
+
+ def __init__(self):
+ super().__init__("News")
+ self.supported_media = [CPSMatchType.GENERIC,
+ CPSMatchType.AUDIO,
+ CPSMatchType.VIDEO,
+ CPSMatchType.TV,
+ CPSMatchType.RADIO,
+ CPSMatchType.NEWS]
+ self.skill_icon = join(dirname(__file__), "ui", "news.png")
+ self.default_bg = join(dirname(__file__), "ui", "bg.jpg")
+
+ def get_intro_message(self):
+ self.speak_dialog("intro")
+
+ # common play
+ def clean_phrase(self, phrase):
+ phrase = self.remove_voc(phrase, "news")
+ phrase = self.remove_voc(phrase, "pt-pt")
+ phrase = self.remove_voc(phrase, "en-au")
+ phrase = self.remove_voc(phrase, "en-us")
+ phrase = self.remove_voc(phrase, "en-ca")
+ phrase = self.remove_voc(phrase, "en-gb")
+ phrase = self.remove_voc(phrase, "es")
+ phrase = self.remove_voc(phrase, "it")
+ phrase = self.remove_voc(phrase, "fr")
+ phrase = self.remove_voc(phrase, "fi")
+ phrase = self.remove_voc(phrase, "de")
+ phrase = self.remove_voc(phrase, "sv")
+ phrase = self.remove_voc(phrase, "ru")
+ phrase = self.remove_voc(phrase, "nl")
+ phrase = self.remove_voc(phrase, "en")
+ phrase = self.remove_voc(phrase, "ca")
+ return phrase.strip()
+
+ def match_lang(self, phrase):
+ lang = None
+ if self.voc_match(phrase, "pt-pt"):
+ lang = "pt-pt"
+ elif self.voc_match(phrase, "en-au"):
+ lang = "en-au"
+ elif self.voc_match(phrase, "en-us"):
+ lang = "en-us"
+ elif self.voc_match(phrase, "en-gb"):
+ lang = "en-gb"
+ elif self.voc_match(phrase, "en-ca"):
+ lang = "en-ca"
+ elif self.voc_match(phrase, "es"):
+ lang = "es"
+ elif self.voc_match(phrase, "ca"):
+ lang = "ca"
+ elif self.voc_match(phrase, "de"):
+ lang = "de"
+ elif self.voc_match(phrase, "nl"):
+ lang = "nl"
+ elif self.voc_match(phrase, "fi"):
+ lang = "fi"
+ elif self.voc_match(phrase, "sv"):
+ lang = "sv"
+ elif self.voc_match(phrase, "fr"):
+ lang = "fr"
+ elif self.voc_match(phrase, "ru"):
+ lang = "ru"
+ elif self.voc_match(phrase, "it"):
+ lang = "it"
+ elif self.voc_match(phrase, "en"):
+ lang = "en"
+ return lang
+
+ def CPS_search(self, phrase, media_type):
+ """Analyze phrase to see if it is a play-able phrase with this skill.
+
+ Arguments:
+ phrase (str): User phrase uttered after "Play", e.g. "some music"
+ media_type (CPSMatchType): requested CPSMatchType to search for
+
+ Returns:
+ search_results (list): list of dictionaries with result entries
+ {
+ "match_confidence": CPSMatchConfidence.HIGH,
+ "media_type": CPSMatchType.MUSIC,
+ "uri": "https://audioservice.or.gui.will.play.this",
+ "playback": CPSPlayback.GUI,
+ "image": "http://optional.audioservice.jpg",
+ "bg_image": "http://optional.audioservice.background.jpg"
+ }
+ """
+ # requested language
+ lang = self.match_lang(phrase)
+
+ # base score
+ score = 0
+ if media_type == CPSMatchType.NEWS or self.voc_match(phrase, "news"):
+ score = 50
+ # score penalty if media_type is vague
+ elif media_type == CPSMatchType.GENERIC or \
+ media_type == CPSMatchType.VIDEO:
+ score -= 30
+ elif media_type == CPSMatchType.RADIO:
+ score -= 20
+
+ if self.voc_match(phrase, "euro"):
+ # euronews matches take a little longer to extract the streans
+ self.CPS_extend_timeout(1)
+
+ phrase = self.clean_phrase(phrase)
+
+ # default feed (gets score bonus)
+ default_feed = None
+ if not phrase:
+ # "play {lang} news"
+ # choose default feed for requested language
+ if lang and lang in self.langdefaults:
+ default_feed = self.langdefaults[lang]
+ else:
+ # "play the news" -> no feed requested
+ # play user preference if set in skill settings
+ default_feed = self.settings.get("default_feed")
+
+ # score individual results
+ candidates = []
+ lang = lang or self.lang
+ for l in self.lang2news:
+ for k, v in self.lang2news[l].items():
+ # match name
+ _, alias_score = match_one(phrase, v["aliases"])
+ v["match_confidence"] = score + alias_score * 60
+
+ # match languages
+ if lang == l or lang.split("-")[0] == l:
+ v["match_confidence"] += 15 # lang bonus
+ elif lang in v.get("secondary_langs", []) or \
+ lang.split("-")[0] in v.get("secondary_langs", []):
+ v["match_confidence"] += 10 # smaller lang bonus
+ else:
+ v["match_confidence"] -= 20 # wrong language penalty
+
+ # match media type
+ if media_type not in v["match_types"]:
+ # filter audio / video if explicitly asked one or the other
+ v["match_confidence"] = 0
+
+ # favour GUI results over audio only
+ # meant to influence only matches that provide both options
+ if v["playback"] == CPSPlayback.GUI and\
+ media_type not in [CPSMatchType.AUDIO, CPSMatchType.RADIO]:
+ v["match_confidence"] += 5
+
+ # default news feed gets a nice bonus
+ # only happens if phrase doesnt really contain a query
+ if default_feed and k == default_feed:
+ v["match_confidence"] += 30
+
+ # final score
+ v["match_confidence"] = min([v["match_confidence"], 100])
+
+ if v["match_confidence"] >= CPSMatchConfidence.AVERAGE:
+ if callable(v["uri"]):
+ if v.get("rss_feed"):
+ v["uri"] = v["uri"](v["rss_feed"])
+ elif v.get("youtube_channel"):
+ v["uri"] = v["uri"](v["youtube_channel"])
+ else:
+ v["uri"] = v["uri"]()
+
+ if v["uri"]:
+ v["title"] = v.get("title") or k
+ v["bg_image"] = v.get("bg_image") or self.default_bg
+ v["skill_logo"] = self.skill_icon
+ candidates.append(v)
+
+ return candidates
+
+
+def create_skill():
+ return NewsSkill()
diff --git a/gui.png b/gui.png
new file mode 100644
index 0000000..73995f7
Binary files /dev/null and b/gui.png differ
diff --git a/gui2.png b/gui2.png
new file mode 100644
index 0000000..74b90ef
Binary files /dev/null and b/gui2.png differ
diff --git a/gui3.png b/gui3.png
new file mode 100644
index 0000000..f70be16
Binary files /dev/null and b/gui3.png differ
diff --git a/locale/en-us/ca.voc b/locale/en-us/ca.voc
new file mode 100644
index 0000000..6205794
--- /dev/null
+++ b/locale/en-us/ca.voc
@@ -0,0 +1,2 @@
+Catalan
+Catalonia
\ No newline at end of file
diff --git a/locale/en-us/de.voc b/locale/en-us/de.voc
new file mode 100644
index 0000000..36ffaed
--- /dev/null
+++ b/locale/en-us/de.voc
@@ -0,0 +1 @@
+German
\ No newline at end of file
diff --git a/locale/en-us/en-au.voc b/locale/en-us/en-au.voc
new file mode 100644
index 0000000..11be908
--- /dev/null
+++ b/locale/en-us/en-au.voc
@@ -0,0 +1,2 @@
+australia
+australian
\ No newline at end of file
diff --git a/locale/en-us/en-ca.voc b/locale/en-us/en-ca.voc
new file mode 100644
index 0000000..b8a98dc
--- /dev/null
+++ b/locale/en-us/en-ca.voc
@@ -0,0 +1,2 @@
+canada
+canadian
\ No newline at end of file
diff --git a/locale/en-us/en-gb.voc b/locale/en-us/en-gb.voc
new file mode 100644
index 0000000..b0b9b93
--- /dev/null
+++ b/locale/en-us/en-gb.voc
@@ -0,0 +1,3 @@
+british
+united kingdom
+UK
\ No newline at end of file
diff --git a/locale/en-us/en-us.voc b/locale/en-us/en-us.voc
new file mode 100644
index 0000000..7300b05
--- /dev/null
+++ b/locale/en-us/en-us.voc
@@ -0,0 +1,3 @@
+america
+american
+united states
\ No newline at end of file
diff --git a/locale/en-us/en.voc b/locale/en-us/en.voc
new file mode 100644
index 0000000..3d38949
--- /dev/null
+++ b/locale/en-us/en.voc
@@ -0,0 +1 @@
+English
\ No newline at end of file
diff --git a/locale/en-us/es.voc b/locale/en-us/es.voc
new file mode 100644
index 0000000..f52eec4
--- /dev/null
+++ b/locale/en-us/es.voc
@@ -0,0 +1 @@
+Spanish
\ No newline at end of file
diff --git a/locale/en-us/euro.voc b/locale/en-us/euro.voc
new file mode 100644
index 0000000..77ddfda
--- /dev/null
+++ b/locale/en-us/euro.voc
@@ -0,0 +1,2 @@
+euro
+european
\ No newline at end of file
diff --git a/locale/en-us/fi.voc b/locale/en-us/fi.voc
new file mode 100644
index 0000000..275b517
--- /dev/null
+++ b/locale/en-us/fi.voc
@@ -0,0 +1,2 @@
+finland
+finnish
\ No newline at end of file
diff --git a/locale/en-us/fr.voc b/locale/en-us/fr.voc
new file mode 100644
index 0000000..da67a98
--- /dev/null
+++ b/locale/en-us/fr.voc
@@ -0,0 +1,2 @@
+French
+france
\ No newline at end of file
diff --git a/locale/en-us/intro.dialog b/locale/en-us/intro.dialog
new file mode 100644
index 0000000..9c9901c
--- /dev/null
+++ b/locale/en-us/intro.dialog
@@ -0,0 +1 @@
+thank you for installing News Skill
\ No newline at end of file
diff --git a/locale/en-us/it.voc b/locale/en-us/it.voc
new file mode 100644
index 0000000..16c4436
--- /dev/null
+++ b/locale/en-us/it.voc
@@ -0,0 +1,2 @@
+Italian
+Italy
\ No newline at end of file
diff --git a/locale/en-us/news.voc b/locale/en-us/news.voc
new file mode 100644
index 0000000..7ee44d9
--- /dev/null
+++ b/locale/en-us/news.voc
@@ -0,0 +1 @@
+news
\ No newline at end of file
diff --git a/locale/en-us/nl.voc b/locale/en-us/nl.voc
new file mode 100644
index 0000000..c8e45e1
--- /dev/null
+++ b/locale/en-us/nl.voc
@@ -0,0 +1,3 @@
+dutch
+Nederlands
+Netherlands
\ No newline at end of file
diff --git a/locale/en-us/pt-pt.voc b/locale/en-us/pt-pt.voc
new file mode 100644
index 0000000..ac1b7ed
--- /dev/null
+++ b/locale/en-us/pt-pt.voc
@@ -0,0 +1,2 @@
+Portuguese
+Portugal
\ No newline at end of file
diff --git a/locale/en-us/ru.voc b/locale/en-us/ru.voc
new file mode 100644
index 0000000..3266fc1
--- /dev/null
+++ b/locale/en-us/ru.voc
@@ -0,0 +1,2 @@
+russian
+russia
\ No newline at end of file
diff --git a/locale/en-us/sv.voc b/locale/en-us/sv.voc
new file mode 100644
index 0000000..35758df
--- /dev/null
+++ b/locale/en-us/sv.voc
@@ -0,0 +1,2 @@
+sweden
+swedish
\ No newline at end of file
diff --git a/locale/en-us/video.voc b/locale/en-us/video.voc
new file mode 100644
index 0000000..a227d29
--- /dev/null
+++ b/locale/en-us/video.voc
@@ -0,0 +1 @@
+video
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..ed217b0
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,5 @@
+feedparser
+youtube-dl
+pafy
+ovos_utils>=0.0.8a1
+youtube_searcher>=0.1.5
\ No newline at end of file
diff --git a/res/desktop/skill-news.desktop b/res/desktop/skill-news.desktop
new file mode 100644
index 0000000..be22776
--- /dev/null
+++ b/res/desktop/skill-news.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Terminal=false
+Type=Application
+Name=Euro News Skill
+Exec=mycroft-gui-app --hideTextInput --skill=skill-news.jarbasskills.home
+Icon=news.png
+Categories=VoiceApp
+StartupNotify=false
+X-DBUS-StartupType=None
+X-KDE-StartupNotify=false
diff --git a/res/desktop/skill.json b/res/desktop/skill.json
new file mode 100644
index 0000000..1f28b09
--- /dev/null
+++ b/res/desktop/skill.json
@@ -0,0 +1,26 @@
+{
+ "name": "News Streams Skill",
+ "skillname": "skill-news",
+ "authorname": "JarbasSkills",
+ "foldername": "",
+ "url": "https://github.com/JarbasSkills/skill-news",
+ "branch": "v0.3",
+ "desktopFile": true,
+ "warning": "",
+ "systemDeps": false,
+ "platforms": [
+ "arm",
+ "arm64",
+ "i386",
+ "x86_64",
+ "ia64"
+ ],
+ "examples": [
+ "play the news",
+ "play npr news",
+ "play euronews",
+ "play catalan news",
+ "play portuguese news",
+ "play news in spanish"
+ ]
+}
diff --git a/res/icon/news.png b/res/icon/news.png
new file mode 100644
index 0000000..788b26b
Binary files /dev/null and b/res/icon/news.png differ
diff --git a/skill_requirements.txt b/skill_requirements.txt
new file mode 100644
index 0000000..96df3b4
--- /dev/null
+++ b/skill_requirements.txt
@@ -0,0 +1 @@
+https://github.com/JarbasSkills/skill-better-playback-control
\ No newline at end of file
diff --git a/ui/bg.jpg b/ui/bg.jpg
new file mode 100644
index 0000000..fa7d6ff
Binary files /dev/null and b/ui/bg.jpg differ
diff --git a/ui/images/ABC.png b/ui/images/ABC.png
new file mode 100644
index 0000000..2a78a69
Binary files /dev/null and b/ui/images/ABC.png differ
diff --git a/ui/images/AP.png b/ui/images/AP.png
new file mode 100644
index 0000000..013f86f
Binary files /dev/null and b/ui/images/AP.png differ
diff --git a/ui/images/BBC.png b/ui/images/BBC.png
new file mode 100644
index 0000000..b0bb7b2
Binary files /dev/null and b/ui/images/BBC.png differ
diff --git a/ui/images/CBC.png b/ui/images/CBC.png
new file mode 100644
index 0000000..9b688e4
Binary files /dev/null and b/ui/images/CBC.png differ
diff --git a/ui/images/CCMA.png b/ui/images/CCMA.png
new file mode 100644
index 0000000..ceae3aa
Binary files /dev/null and b/ui/images/CCMA.png differ
diff --git a/ui/images/DLF.png b/ui/images/DLF.png
new file mode 100644
index 0000000..d1df6e5
Binary files /dev/null and b/ui/images/DLF.png differ
diff --git a/ui/images/Ekot.png b/ui/images/Ekot.png
new file mode 100755
index 0000000..8b9db69
Binary files /dev/null and b/ui/images/Ekot.png differ
diff --git a/ui/images/FOX.png b/ui/images/FOX.png
new file mode 100644
index 0000000..9791d9f
Binary files /dev/null and b/ui/images/FOX.png differ
diff --git a/ui/images/NPR.png b/ui/images/NPR.png
new file mode 100644
index 0000000..93e08ba
Binary files /dev/null and b/ui/images/NPR.png differ
diff --git a/ui/images/PBS.png b/ui/images/PBS.png
new file mode 100644
index 0000000..2d24fec
Binary files /dev/null and b/ui/images/PBS.png differ
diff --git a/ui/images/WDR.png b/ui/images/WDR.png
new file mode 100644
index 0000000..20a85c5
Binary files /dev/null and b/ui/images/WDR.png differ
diff --git a/ui/images/Yle.png b/ui/images/Yle.png
new file mode 100644
index 0000000..467a4b3
Binary files /dev/null and b/ui/images/Yle.png differ
diff --git a/ui/images/euronews.png b/ui/images/euronews.png
new file mode 100644
index 0000000..f795803
Binary files /dev/null and b/ui/images/euronews.png differ
diff --git a/ui/images/gpb.png b/ui/images/gpb.png
new file mode 100644
index 0000000..2fd522e
Binary files /dev/null and b/ui/images/gpb.png differ
diff --git a/ui/images/oe3.jpeg b/ui/images/oe3.jpeg
new file mode 100644
index 0000000..6475daa
Binary files /dev/null and b/ui/images/oe3.jpeg differ
diff --git a/ui/images/rdp_africa.png b/ui/images/rdp_africa.png
new file mode 100644
index 0000000..e34c2b2
Binary files /dev/null and b/ui/images/rdp_africa.png differ
diff --git a/ui/images/rne.png b/ui/images/rne.png
new file mode 100644
index 0000000..7b19272
Binary files /dev/null and b/ui/images/rne.png differ
diff --git a/ui/images/skystream.png b/ui/images/skystream.png
new file mode 100644
index 0000000..bf7848d
Binary files /dev/null and b/ui/images/skystream.png differ
diff --git a/ui/images/tsf.png b/ui/images/tsf.png
new file mode 100644
index 0000000..86dc693
Binary files /dev/null and b/ui/images/tsf.png differ
diff --git a/ui/images/twc.png b/ui/images/twc.png
new file mode 100644
index 0000000..385e91d
Binary files /dev/null and b/ui/images/twc.png differ
diff --git a/ui/images/vrt.png b/ui/images/vrt.png
new file mode 100644
index 0000000..0d1ccfc
Binary files /dev/null and b/ui/images/vrt.png differ
diff --git a/ui/news.png b/ui/news.png
new file mode 100644
index 0000000..788b26b
Binary files /dev/null and b/ui/news.png differ