Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-podcastparser for openSUSE:Factory checked in at 2023-01-03 15:05:21 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-podcastparser (Old) and /work/SRC/openSUSE:Factory/.python-podcastparser.new.1563 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-podcastparser" Tue Jan 3 15:05:21 2023 rev:7 rq:1046268 version:0.6.9 Changes: -------- --- /work/SRC/openSUSE:Factory/python-podcastparser/python-podcastparser.changes 2022-01-16 23:19:15.822377442 +0100 +++ /work/SRC/openSUSE:Factory/.python-podcastparser.new.1563/python-podcastparser.changes 2023-01-03 15:05:38.982609437 +0100 @@ -1,0 +2,6 @@ +Mon Jan 2 18:50:29 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 0.6.9: + * add support for itunes categories, seasons and keywords + +------------------------------------------------------------------- Old: ---- podcastparser-0.6.8.tar.gz New: ---- podcastparser-0.6.9.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-podcastparser.spec ++++++ --- /var/tmp/diff_new_pack.zFPxae/_old 2023-01-03 15:05:39.494612426 +0100 +++ /var/tmp/diff_new_pack.zFPxae/_new 2023-01-03 15:05:39.502612472 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-podcastparser # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-podcastparser -Version: 0.6.8 +Version: 0.6.9 Release: 0 Summary: A podcast parser License: ISC ++++++ podcastparser-0.6.8.tar.gz -> podcastparser-0.6.9.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/PKG-INFO new/podcastparser-0.6.9/PKG-INFO --- old/podcastparser-0.6.8/PKG-INFO 2021-09-23 07:56:03.069217400 +0200 +++ new/podcastparser-0.6.9/PKG-INFO 2022-12-14 08:00:25.560205000 +0100 @@ -1,12 +1,11 @@ Metadata-Version: 2.1 Name: podcastparser -Version: 0.6.8 +Version: 0.6.9 Summary: Simplified, fast RSS parser Home-page: http://gpodder.org/podcastparser/ Author: Thomas Perl Author-email: m...@thp.io License: ISC License -Platform: UNKNOWN Description-Content-Type: text/markdown License-File: LICENSE License-File: AUTHORS @@ -30,5 +29,3 @@ Then create an (annotated) tag and push it. The GitHub action will publish on pypi. - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/doc/index.rst new/podcastparser-0.6.9/doc/index.rst --- old/podcastparser-0.6.8/doc/index.rst 2021-09-23 07:55:47.000000000 +0200 +++ new/podcastparser-0.6.9/doc/index.rst 2022-12-14 08:00:09.000000000 +0100 @@ -89,6 +89,9 @@ **rss/channel/itunes:type** Podcast type (whitespace is squashed). One of 'episodic' or 'serial'. +**rss/channel/itunes:keywords** + Podcast keywords (whitespace is squashed). + **rss/channel/atom:link@rel=payment** Podcast payment URL (e.g. Flattr). @@ -105,6 +108,9 @@ The podcast owner contact information. The <itunes:owner> tag information is for administrative communication about the podcast and isn't displayed in Apple Podcasts +**rss/channel/itunes:category** + The show category information. + **rss/channel/itunes:explicit** Indicates whether podcast contains explicit material. @@ -184,6 +190,9 @@ **rss/channel/item/itunes:author** The group responsible for creating the episode. +**rss/channel/item/itunes:season** + The season number of the episode. + **rss/channel/item/itunes:episode** An episode number. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/podcastparser.egg-info/PKG-INFO new/podcastparser-0.6.9/podcastparser.egg-info/PKG-INFO --- old/podcastparser-0.6.8/podcastparser.egg-info/PKG-INFO 2021-09-23 07:56:03.000000000 +0200 +++ new/podcastparser-0.6.9/podcastparser.egg-info/PKG-INFO 2022-12-14 08:00:25.000000000 +0100 @@ -1,12 +1,11 @@ Metadata-Version: 2.1 Name: podcastparser -Version: 0.6.8 +Version: 0.6.9 Summary: Simplified, fast RSS parser Home-page: http://gpodder.org/podcastparser/ Author: Thomas Perl Author-email: m...@thp.io License: ISC License -Platform: UNKNOWN Description-Content-Type: text/markdown License-File: LICENSE License-File: AUTHORS @@ -30,5 +29,3 @@ Then create an (annotated) tag and push it. The GitHub action will publish on pypi. - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/podcastparser.egg-info/SOURCES.txt new/podcastparser-0.6.9/podcastparser.egg-info/SOURCES.txt --- old/podcastparser-0.6.8/podcastparser.egg-info/SOURCES.txt 2021-09-23 07:56:03.000000000 +0200 +++ new/podcastparser-0.6.9/podcastparser.egg-info/SOURCES.txt 2022-12-14 08:00:25.000000000 +0100 @@ -36,6 +36,8 @@ tests/data/episode_number.rss tests/data/episode_number_invalid.json tests/data/episode_number_invalid.rss +tests/data/episode_season.json +tests/data/episode_season.rss tests/data/episode_type.json tests/data/episode_type.rss tests/data/episode_type_invalid.json @@ -62,10 +64,16 @@ tests/data/image_url_on_newline.rss tests/data/itunes_author.json tests/data/itunes_author.rss +tests/data/itunes_categories.json +tests/data/itunes_categories.rss +tests/data/itunes_categories_empty.json +tests/data/itunes_categories_empty.rss tests/data/itunes_duration.json tests/data/itunes_duration.rss tests/data/itunes_episode_image.json tests/data/itunes_episode_image.rss +tests/data/itunes_keywords.json +tests/data/itunes_keywords.rss tests/data/itunes_no_xmlns.json tests/data/itunes_no_xmlns.rss tests/data/itunes_owner.json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/podcastparser.py new/podcastparser-0.6.9/podcastparser.py --- old/podcastparser-0.6.8/podcastparser.py 2021-09-23 07:55:47.000000000 +0200 +++ new/podcastparser-0.6.9/podcastparser.py 2022-12-14 08:00:09.000000000 +0100 @@ -22,7 +22,7 @@ # Will be parsed by setup.py to determine package metadata __author__ = 'Thomas Perl <m...@thp.io>' -__version__ = '0.6.8' +__version__ = '0.6.9' __website__ = 'http://gpodder.org/podcastparser/' __license__ = 'ISC License' @@ -32,38 +32,10 @@ import os import time -try: - # Python 2 - from htmlentitydefs import entitydefs - entitydefs = dict((key, value.decode('latin-1')) for key, value in entitydefs.iteritems()) - chr = unichr -except ImportError: - # Python 3 - from html.entities import entitydefs - -try: - # Python 2 - import urlparse -except ImportError: - # Python 3 - from urllib import parse as urlparse - -try: - # Python 2 - from rfc822 import parsedate_tz - import calendar - # This is taken from Python 3's email._parseaddr, since it handles - # pre-epoch dates better than what Python 2 does (time.mktime()) - def mktime_tz(data): - if data[9] is None: - # No zone info, so localtime is better assumption than GMT - return time.mktime(data[:8] + (-1,)) - else: - t = calendar.timegm(data) - return t - data[9] -except ImportError: - # Python 3 - from email.utils import mktime_tz, parsedate_tz +from html.entities import entitydefs + +from urllib import parse as urlparse +from email.utils import mktime_tz, parsedate_tz import logging logger = logging.getLogger(__name__) @@ -106,6 +78,12 @@ def end(self, handler, text): handler.set_podcast_attr(self.key, self.filter_func(text)) +class PodcastAttrList(Target): + WANT_TEXT = True + + def end(self, handler, text): + handler.set_podcast_attr(self.key, self.filter_func(text).split(', ')) + class PodcastAttrType(Target): WANT_TEXT = True @@ -185,6 +163,15 @@ ATTRIBUTE = 'url' +class EpisodeAttrSeason(EpisodeAttr): + def end(self, handler, text): + try: + episode_season = int(text) + except ValueError: + episode_season = 0 + handler.set_episode_attr(self.key, episode_season) + + class EpisodeAttrNumber(EpisodeAttr): def end(self, handler, text): value = self.filter_func(text) @@ -331,6 +318,22 @@ handler.append_itunes_owner(self.key, self.filter_func(text)) +class ItunesCategoryAttr(Target): + def start(self, handler, attrs): + # Let's use an empty string as a fallback for first-level categories + # in case there is a valid sub-category. + value = attrs.get('text', '') + handler.append_itunes_category(self.key, self.filter_func(value)) + + +class ItunesSubCategoryAttr(Target): + def start(self, handler, attrs): + value = attrs.get('text') + if not value: + return + handler.append_itunes_subcategory(self.key, self.filter_func(value)) + + class ItunesOwnerItem(Target): def start(self, handler, attrs): handler.add_itunes_owner() @@ -658,8 +661,8 @@ try: pubtimeseconds = int(mktime_tz(parsed)) return pubtimeseconds - except(OverflowError,ValueError): - logger.warning('bad pubdate %s is before epoch or after end of time (2038)',parsed) + except(OverflowError, ValueError): + logger.warning('bad pubdate %s is before epoch or after end of time (2038)', parsed) return 0 try: @@ -699,10 +702,16 @@ 'rss/channel/atom:link': PodcastAtomLink(), 'rss/channel/generator': PodcastAttr('generator', squash_whitespace), 'rss/channel/language': PodcastAttr('language', squash_whitespace), + + 'rss/channel/itunes:category': ItunesCategoryAttr('itunes_categories'), + 'rss/channel/itunes:category/itunes:category': ItunesSubCategoryAttr('itunes_categories'), + 'rss/channel/itunes:category/itunes:category/itunes:category': ItunesSubCategoryAttr('itunes_categories'), + 'rss/channel/itunes:author': PodcastAttr('itunes_author', squash_whitespace), 'rss/channel/itunes:owner': ItunesOwnerItem('itunes_owner', squash_whitespace), 'rss/channel/itunes:explicit': PodcastAttrExplicit('explicit', squash_whitespace), 'rss/channel/itunes:new-feed-url': PodcastAttr('new_url', squash_whitespace), + 'rss/channel/itunes:keywords': PodcastAttrList('itunes_keywords', squash_whitespace), 'rss/redirect/newLocation': PodcastAttr('new_url', squash_whitespace), 'rss/channel/itunes:owner/itunes:email': ItunesOwnerAttr('email', squash_whitespace), @@ -722,6 +731,7 @@ 'rss/channel/item/atom:link': AtomLink(), 'rss/channel/item/itunes:explicit': EpisodeAttrExplicit('explicit', squash_whitespace), 'rss/channel/item/itunes:author': EpisodeAttr('itunes_author', squash_whitespace), + 'rss/channel/item/itunes:season': EpisodeAttrSeason('season', squash_whitespace), 'rss/channel/item/itunes:episode': EpisodeAttrNumber('number', squash_whitespace), 'rss/channel/item/itunes:episodeType': EpisodeAttrType('type', squash_whitespace), @@ -868,6 +878,16 @@ def append_itunes_owner(self, key, value): self.data['itunes_owner'][key] = value + def add_itunes_categories(self): + self.data['itunes_categories'] = [] + + def append_itunes_category(self, key, value): + self.data.setdefault('itunes_categories', []).append([value]) + + def append_itunes_subcategory(self, key, value): + entry = self.data['itunes_categories'][-1] + entry.append(value) + def startElement(self, name, attrs): self.namespace = Namespace(attrs, self.namespace) name = self.namespace.map(name) @@ -988,7 +1008,10 @@ # urlunsplit might return "a slighty different, but equivalent URL" return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) + HTML_TEST = re.compile(r'<[a-z][a-z0-9]*(?:\s.*?>|\/?>)', re.IGNORECASE | re.DOTALL) + + def is_html(text): """Heuristically tell if text is HTML @@ -1000,6 +1023,7 @@ """ return bool(HTML_TEST.search(text)) + def remove_html_tags(html): """ Remove HTML tags from a string and replace numeric and diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/test_podcastparser.py new/podcastparser-0.6.9/test_podcastparser.py --- old/podcastparser-0.6.8/test_podcastparser.py 2021-09-23 07:55:47.000000000 +0200 +++ new/podcastparser-0.6.9/test_podcastparser.py 2022-12-14 08:00:09.000000000 +0100 @@ -21,12 +21,7 @@ import os import glob import json -try: - # Python 2 - from StringIO import StringIO -except ImportError: - # Python 3 - from io import StringIO +import io import pytest @@ -64,5 +59,5 @@ @pytest.mark.parametrize("feed", feeds) def test_fail_parse(self, feed): with pytest.raises(podcastparser.FeedParseError): - podcastparser.parse('file://example.com/feed.xml', StringIO(feed)) + podcastparser.parse('file://example.com/feed.xml', io.StringIO(feed)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/tests/data/episode_season.json new/podcastparser-0.6.9/tests/data/episode_season.json --- old/podcastparser-0.6.8/tests/data/episode_season.json 1970-01-01 01:00:00.000000000 +0100 +++ new/podcastparser-0.6.9/tests/data/episode_season.json 2022-12-14 08:00:09.000000000 +0100 @@ -0,0 +1,22 @@ +{ + "title": "episode_season", + "episodes": [ + { + "title": "Episode", + "description": "", + "published": 0, + "guid": "http://example.org/example.mp3", + "link": "", + "total_time": 0, + "payment_url": null, + "season": 1, + "enclosures": [ + { + "file_size": -1, + "url": "http://example.org/example.mp3", + "mime_type": "application/octet-stream" + } + ] + } + ] + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/tests/data/episode_season.rss new/podcastparser-0.6.9/tests/data/episode_season.rss --- old/podcastparser-0.6.8/tests/data/episode_season.rss 1970-01-01 01:00:00.000000000 +0100 +++ new/podcastparser-0.6.9/tests/data/episode_season.rss 2022-12-14 08:00:09.000000000 +0100 @@ -0,0 +1,9 @@ +<rss> + <channel> + <item> + <title>Episode</title> + <enclosure url="http://example.org/example.mp3"/> + <itunes:season>1</itunes:season> + </item> + </channel> +</rss> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/tests/data/itunes_categories.json new/podcastparser-0.6.9/tests/data/itunes_categories.json --- old/podcastparser-0.6.8/tests/data/itunes_categories.json 1970-01-01 01:00:00.000000000 +0100 +++ new/podcastparser-0.6.9/tests/data/itunes_categories.json 2022-12-14 08:00:09.000000000 +0100 @@ -0,0 +1,9 @@ +{ + "title": "itunes_categories", + "episodes": [], + "itunes_categories": [ + ["Business", "Entrepreneurship", "Ecommerce"], + ["Education"], + ["Society & Culture"] + ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/tests/data/itunes_categories.rss new/podcastparser-0.6.9/tests/data/itunes_categories.rss --- old/podcastparser-0.6.8/tests/data/itunes_categories.rss 1970-01-01 01:00:00.000000000 +0100 +++ new/podcastparser-0.6.9/tests/data/itunes_categories.rss 2022-12-14 08:00:09.000000000 +0100 @@ -0,0 +1,11 @@ +<rss> + <channel> + <itunes:category text="Business"> + <itunes:category text="Entrepreneurship"> + <itunes:category text="Ecommerce" /> + </itunes:category> + </itunes:category> + <itunes:category text="Education"/> + <itunes:category text="Society & Culture"/> + </channel> +</rss> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/tests/data/itunes_categories_empty.json new/podcastparser-0.6.9/tests/data/itunes_categories_empty.json --- old/podcastparser-0.6.8/tests/data/itunes_categories_empty.json 1970-01-01 01:00:00.000000000 +0100 +++ new/podcastparser-0.6.9/tests/data/itunes_categories_empty.json 2022-12-14 08:00:09.000000000 +0100 @@ -0,0 +1,7 @@ +{ + "title": "itunes_categories_empty", + "episodes": [], + "itunes_categories": [ + ["", "Religion & Spirituality"] + ] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/tests/data/itunes_categories_empty.rss new/podcastparser-0.6.9/tests/data/itunes_categories_empty.rss --- old/podcastparser-0.6.8/tests/data/itunes_categories_empty.rss 1970-01-01 01:00:00.000000000 +0100 +++ new/podcastparser-0.6.9/tests/data/itunes_categories_empty.rss 2022-12-14 08:00:09.000000000 +0100 @@ -0,0 +1,7 @@ +<rss> + <channel> + <itunes:category> + <itunes:category text="Religion & Spirituality"/> + </itunes:category> + </channel> +</rss> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/tests/data/itunes_keywords.json new/podcastparser-0.6.9/tests/data/itunes_keywords.json --- old/podcastparser-0.6.8/tests/data/itunes_keywords.json 1970-01-01 01:00:00.000000000 +0100 +++ new/podcastparser-0.6.9/tests/data/itunes_keywords.json 2022-12-14 08:00:09.000000000 +0100 @@ -0,0 +1,5 @@ +{ + "title": "itunes_keywords", + "episodes": [], + "itunes_keywords": ["adventure", "tabletop", "rpg"] +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/podcastparser-0.6.8/tests/data/itunes_keywords.rss new/podcastparser-0.6.9/tests/data/itunes_keywords.rss --- old/podcastparser-0.6.8/tests/data/itunes_keywords.rss 1970-01-01 01:00:00.000000000 +0100 +++ new/podcastparser-0.6.9/tests/data/itunes_keywords.rss 2022-12-14 08:00:09.000000000 +0100 @@ -0,0 +1,5 @@ +<rss> + <channel> + <itunes:keywords>adventure, tabletop, rpg</itunes:keywords> + </channel> +</rss>