Hello community,

here is the log from the commit of package sacad for openSUSE:Factory checked 
in at 2020-06-14 18:33:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/sacad (Old)
 and      /work/SRC/openSUSE:Factory/.sacad.new.3606 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "sacad"

Sun Jun 14 18:33:53 2020 rev:2 rq:814322 version:2.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/sacad/sacad.changes      2020-03-26 
23:33:08.258772083 +0100
+++ /work/SRC/openSUSE:Factory/.sacad.new.3606/sacad.changes    2020-06-14 
18:35:47.190633210 +0200
@@ -1,0 +2,6 @@
+Sat Jun 13 11:19:24 UTC 2020 - Martin Hauke <[email protected]>
+
+- Update to version 2.3.0
+  * Add Deezer cover source
+
+-------------------------------------------------------------------

Old:
----
  sacad-2.2.3.tar.gz

New:
----
  sacad-2.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ sacad.spec ++++++
--- /var/tmp/diff_new_pack.yRQOhI/_old  2020-06-14 18:35:47.854635324 +0200
+++ /var/tmp/diff_new_pack.yRQOhI/_new  2020-06-14 18:35:47.858635337 +0200
@@ -1,7 +1,7 @@
 #
-# spec file for package python-sacad
+# spec file for package sacad
 #
-# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -12,11 +12,12 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
+#
 
 
 Name:           sacad
-Version:        2.2.3
+Version:        2.3.0
 Release:        0
 Summary:        Search and download music album covers
 License:        MPL-2.0
@@ -24,9 +25,11 @@
 URL:            https://github.com/desbma/sacad
 Source:         
https://github.com/desbma/sacad/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz
 BuildRequires:  fdupes
+BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 BuildRequires:  python3-setuptools
-BuildRequires:  fdupes
+Requires:       python3-Pillow >= 2.7.0
+Requires:       python3-Unidecode >= 1.1.1
 Requires:       python3-aiohttp >= 3.6
 Requires:       python3-appdirs >= 1.4.0
 Requires:       python3-bitarray >= 0.8.3
@@ -34,9 +37,7 @@
 Requires:       python3-fake-useragent >= 0.1.11
 Requires:       python3-lxml >= 4.0.0
 Requires:       python3-mutagen >= 1.31
-Requires:       python3-Pillow >= 2.7.0
 Requires:       python3-tqdm >= 4.28.1
-Requires:       python3-Unidecode >= 1.1.1
 Requires:       python3-web_cache >= 1.1.0
 BuildArch:      noarch
 

++++++ sacad-2.2.3.tar.gz -> sacad-2.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/README.md new/sacad-2.3.0/README.md
--- old/sacad-2.2.3/README.md   2019-11-30 20:51:20.000000000 +0100
+++ new/sacad-2.3.0/README.md   2020-06-08 01:13:37.000000000 +0200
@@ -24,11 +24,12 @@
 * Support JPEG and PNG formats
 * Customizable output: save image along with the audio files / in a different 
directory named by artist/album / embed cover in audio files...
 * Currently support the following cover sources:
-    * Last.fm
-    * Google Images
-    * ~~CoverLib~~ (site is down)
     * Amazon CD (.com, .ca, .cn, .fr, .de, .co.jp and .co.uk variants)
     * Amazon digital music
+    * ~~CoverLib~~ (site is down)
+    * Deezer
+    * Google Images
+    * Last.fm
 * Smart sorting algorithm to select THE best cover for a given query, using 
several factors: source reliability, image format, image size, image similarity 
with reference cover, etc.
 * Automatically crunch images with optipng or jpegoptim (can save 30% of 
filesize without any loss of quality, great for portable players)
 * Cache search results locally for faster future search
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/sacad/__init__.py 
new/sacad-2.3.0/sacad/__init__.py
--- old/sacad-2.2.3/sacad/__init__.py   2019-11-30 20:51:20.000000000 +0100
+++ new/sacad-2.3.0/sacad/__init__.py   2020-06-08 01:13:37.000000000 +0200
@@ -2,7 +2,7 @@
 
 """ Smart Automatic Cover Art Downloader : search and download music album 
covers. """
 
-__version__ = "2.2.3"
+__version__ = "2.3.0"
 __author__ = "desbma"
 __license__ = "MPL 2.0"
 
@@ -17,11 +17,13 @@
 from sacad.cover import CoverSourceResult, HAS_JPEGOPTIM, HAS_OPTIPNG, 
SUPPORTED_IMG_FORMATS
 
 
-async def search_and_download(album, artist, format, size, out_filepath, *, 
size_tolerance_prct, amazon_tlds, no_lq_sources):
+async def search_and_download(album, artist, format, size, out_filepath, *, 
size_tolerance_prct, amazon_tlds,
+                              no_lq_sources, preserve_format=False):
   """ Search and download a cover, return True if success, False instead. """
   # register sources
   source_args = (size, size_tolerance_prct)
   cover_sources = [sources.LastFmCoverSource(*source_args),
+                   sources.DeezerCoverSource(*source_args),
                    sources.AmazonCdCoverSource(*source_args),
                    sources.AmazonDigitalCoverSource(*source_args)]
   for tld in amazon_tlds:
@@ -58,7 +60,7 @@
   done = False
   for result in results:
     try:
-      await result.get(format, size, size_tolerance_prct, out_filepath)
+      await result.get(format, size, size_tolerance_prct, out_filepath, 
preserve_format=preserve_format)
     except Exception as e:
       logging.getLogger("Main").warning("Download of %s failed: %s %s" % 
(result,
                                                                           
e.__class__.__qualname__,
@@ -101,6 +103,11 @@
                           help="""Disable cover sources that may return 
unreliable results (ie. Google Images).
                                   It will speed up search and improve 
reliability, but may fail to find results for
                                   some difficult searches.""")
+  arg_parser.add_argument("-p",
+                          "--preserve-format",
+                          action="store_true",
+                          default=False,
+                          help="Preserve source image format if possible. 
Target format will still be prefered when sorting results.")
 
 
 def cl_main():
@@ -164,7 +171,8 @@
                                   args.out_filepath,
                                   size_tolerance_prct=args.size_tolerance_prct,
                                   amazon_tlds=args.amazon_tlds,
-                                  no_lq_sources=args.no_lq_sources)
+                                  no_lq_sources=args.no_lq_sources,
+                                  preserve_format=args.preserve_format)
   if hasattr(asyncio, "run"):
     # Python >=3.7.0
     asyncio.run(coroutine)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/sacad/cover.py 
new/sacad-2.3.0/sacad/cover.py
--- old/sacad-2.2.3/sacad/cover.py      2019-11-30 20:51:20.000000000 +0100
+++ new/sacad-2.3.0/sacad/cover.py      2020-06-08 01:13:37.000000000 +0200
@@ -37,6 +37,8 @@
 SUPPORTED_IMG_FORMATS = {"jpg": CoverImageFormat.JPEG,
                          "jpeg": CoverImageFormat.JPEG,
                          "png": CoverImageFormat.PNG}
+FORMAT_EXTENSIONS = {CoverImageFormat.JPEG: "jpg",
+                     CoverImageFormat.PNG: "png"}
 
 
 def is_square(x):
@@ -111,7 +113,7 @@
       s += " [x%u]" % (len(self.urls))
     return s
 
-  async def get(self, target_format, target_size, size_tolerance_prct, 
out_filepath):
+  async def get(self, target_format, target_size, size_tolerance_prct, 
out_filepath, *, preserve_format=False):
     """ Download cover and process it. """
     if self.source_quality.value <= CoverSourceQuality.LOW.value:
       logging.getLogger("Cover").warning("Cover is from a potentially 
unreliable source and may be unrelated to the search")
@@ -143,7 +145,7 @@
                         (abs(max(self.size) - target_size) >
                          target_size * size_tolerance_prct / 100))
     need_join = len(images_data) > 1
-    if need_join or need_format_change or need_size_change:
+    if (need_format_change and (not preserve_format)) or need_join or 
need_size_change:
       # post process
       image_data = self.postProcess(images_data,
                                     target_format if need_format_change else 
None,
@@ -152,7 +154,14 @@
       # crunch image again
       image_data = await __class__.crunch(image_data, target_format)
 
+      format_changed = need_format_change
+    else:
+      format_changed = False
+
     # write it
+    if need_format_change and (not format_changed):
+      assert(preserve_format)
+      out_filepath = "%s.%s" % (os.path.splitext(out_filepath)[0], 
FORMAT_EXTENSIONS[self.format])
     with open(out_filepath, "wb") as file:
       file.write(image_data)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/sacad/recurse.py 
new/sacad-2.3.0/sacad/recurse.py
--- old/sacad-2.2.3/sacad/recurse.py    2019-11-30 20:51:20.000000000 +0100
+++ new/sacad-2.3.0/sacad/recurse.py    2020-06-08 01:13:37.000000000 +0200
@@ -12,6 +12,7 @@
 import logging
 import operator
 import os
+import resource
 import string
 import tempfile
 
@@ -75,7 +76,7 @@
             (self.metadata == other.metadata))
 
 
-def analyze_lib(lib_dir, cover_pattern, *, ignore_existing=False, 
full_scan=False):
+def analyze_lib(lib_dir, cover_pattern, *, ignore_existing=False, 
full_scan=False, all_formats=False):
   """ Recursively analyze library, and return a list of work. """
   work = []
   stats = collections.OrderedDict(((k, 0) for k in("files", "albums", "missing 
covers", "errors")))
@@ -89,7 +90,8 @@
                              rel_filepaths,
                              cover_pattern,
                              ignore_existing=ignore_existing,
-                             full_scan=full_scan)
+                             full_scan=full_scan,
+                             all_formats=all_formats)
       progress.set_postfix(stats, refresh=False)
       progress.update(1)
       work.extend(new_work)
@@ -192,7 +194,7 @@
 
 
 def analyze_dir(stats, parent_dir, rel_filepaths, cover_pattern, *,
-                ignore_existing=False, full_scan=False):
+                ignore_existing=False, full_scan=False, all_formats=False):
   """ Analyze a directory (non recursively) and return a list of Work objects. 
"""
   r = []
 
@@ -222,7 +224,10 @@
     # add work item if needed
     if cover_pattern != EMBEDDED_ALBUM_ART_SYMBOL:
       cover_filepath = pattern_to_filepath(cover_pattern, parent_dir, metadata)
-      missing = (not os.path.isfile(cover_filepath)) or ignore_existing
+      if all_formats:
+        missing = ignore_existing or (not any(os.path.isfile("%s.%s" % 
(os.path.splitext(cover_filepath)[0], ext)) for ext in 
sacad.SUPPORTED_IMG_FORMATS))
+      else:
+        missing = ignore_existing or (not os.path.isfile(cover_filepath))
     else:
       cover_filepath = EMBEDDED_ALBUM_ART_SYMBOL
       missing = (not metadata.has_embedded_cover) or ignore_existing
@@ -328,7 +333,7 @@
     # so work in smaller chunks to avoid hitting fd limit
     # this also updates the progress faster (instead of working on all 
searches, work on finishing the chunk before
     # getting to the next one)
-    work_chunk_length = 16
+    work_chunk_length = 12
     for work_chunk in ichunk(work, work_chunk_length):
       futures = {}
       for i, cur_work in enumerate(work_chunk, i):
@@ -345,7 +350,8 @@
                                               cover_filepath,
                                               
size_tolerance_prct=args.size_tolerance_prct,
                                               amazon_tlds=args.amazon_tlds,
-                                              no_lq_sources=args.no_lq_sources)
+                                              no_lq_sources=args.no_lq_sources,
+                                              
preserve_format=args.preserve_format)
         future = asyncio.ensure_future(coroutine)
         futures[future] = cur_work
 
@@ -421,11 +427,23 @@
   logging_handler.setFormatter(logging_formatter)
   logging.getLogger().addHandler(logging_handler)
 
+  # bump nofile ulimit
+  try:
+    soft_lim, hard_lim = resource.getrlimit(resource.RLIMIT_NOFILE)
+    if ((soft_lim != resource.RLIM_INFINITY) and
+            ((soft_lim < hard_lim) or (hard_lim == resource.RLIM_INFINITY))):
+      resource.setrlimit(resource.RLIMIT_NOFILE, (hard_lim, hard_lim))
+      logging.getLogger().debug("Max open files count set from %u to %u" % 
(soft_lim, hard_lim))
+  except (AttributeError, OSError):
+    # not supported on system
+    pass
+
   # do the job
   work = analyze_lib(args.lib_dir,
                      args.cover_pattern,
                      ignore_existing=args.ignore_existing,
-                     full_scan=args.full_scan)
+                     full_scan=args.full_scan,
+                     all_formats=args.preserve_format)
   get_covers(work, args)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/sacad/sources/__init__.py 
new/sacad-2.3.0/sacad/sources/__init__.py
--- old/sacad-2.2.3/sacad/sources/__init__.py   2019-11-30 20:51:20.000000000 
+0100
+++ new/sacad-2.3.0/sacad/sources/__init__.py   2020-06-08 01:13:37.000000000 
+0200
@@ -1,4 +1,5 @@
 from sacad.sources.amazoncd import AmazonCdCoverSource, 
AmazonCdCoverSourceResult
 from sacad.sources.amazondigital import AmazonDigitalCoverSource, 
AmazonDigitalCoverSourceResult
-from sacad.sources.lastfm import LastFmCoverSource, LastFmCoverSourceResult
+from sacad.sources.deezer import DeezerCoverSource, DeezerCoverSourceResult
 from sacad.sources.google_images import GoogleImagesWebScrapeCoverSource, 
GoogleImagesCoverSourceResult
+from sacad.sources.lastfm import LastFmCoverSource, LastFmCoverSourceResult
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/sacad/sources/amazonbase.py 
new/sacad-2.3.0/sacad/sources/amazonbase.py
--- old/sacad-2.2.3/sacad/sources/amazonbase.py 2019-11-30 20:51:20.000000000 
+0100
+++ new/sacad-2.3.0/sacad/sources/amazonbase.py 2020-06-08 01:13:37.000000000 
+0200
@@ -9,8 +9,8 @@
   def __init__(self, *args, base_domain, **kwargs):
     super().__init__(*args,
                      allow_cookies=True,
-                     min_delay_between_accesses=2 / 3,
-                     jitter_range_ms=(0, 600),
+                     min_delay_between_accesses=2,
+                     jitter_range_ms=(0, 3000),
                      rate_limited_domains=(base_domain,),
                      **kwargs)
     self.current_ua = self.ua.firefox
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/sacad/sources/deezer.py 
new/sacad-2.3.0/sacad/sources/deezer.py
--- old/sacad-2.2.3/sacad/sources/deezer.py     1970-01-01 01:00:00.000000000 
+0100
+++ new/sacad-2.3.0/sacad/sources/deezer.py     2020-06-08 01:13:37.000000000 
+0200
@@ -0,0 +1,76 @@
+import collections
+import json
+import operator
+
+from sacad.cover import CoverImageFormat, CoverImageMetadata, 
CoverSourceQuality, CoverSourceResult
+from sacad.sources.base import CoverSource
+
+
+class DeezerCoverSourceResult(CoverSourceResult):
+
+  def __init__(self, *args, **kwargs):
+    super().__init__(*args, source_quality=CoverSourceQuality.NORMAL, **kwargs)
+
+
+class DeezerCoverSource(CoverSource):
+
+  """
+  Cover source using the official Deezer API.
+
+  https://developers.deezer.com/api/
+  """
+
+  BASE_URL = "https://api.deezer.com/search";
+  COVER_SIZES = {"cover_small": (56, 56),
+                 "cover": (120, 120),
+                 "cover_medium": (250, 250),
+                 "cover_big": (500, 500),
+                 "cover_xl": (1000, 1000)}
+
+  def __init__(self, *args, **kwargs):
+    super().__init__(*args, min_delay_between_accesses=0.1, **kwargs)
+
+  def getSearchUrl(self, album, artist):
+    """ See CoverSource.getSearchUrl. """
+    # build request url
+    search_params = collections.OrderedDict()
+    search_params["artist"] = artist
+    search_params["album"] = album
+    url_params = collections.OrderedDict()
+    #url_params["strict"] = "on"
+    url_params["order"] = "RANKING"
+    url_params["q"] = " ".join("%s:\"%s\"" % (k, v) for k, v in 
search_params.items())
+    return __class__.assembleUrl(__class__.BASE_URL, url_params)
+
+  def processQueryString(self, s):
+    """ See CoverSource.processQueryString. """
+    # API search is fuzzy, not need to alter query
+    return s
+
+  async def parseResults(self, api_data):
+    """ See CoverSource.parseResults. """
+    results = []
+
+    # get unique albums
+    json_data = json.loads(api_data)
+    albums = []
+    for e in json_data["data"]:
+      album = e["album"]
+      album_id = album["id"]
+      if album_id in map(operator.itemgetter("id"), albums):
+        continue
+      albums.append(album)
+
+    for rank, album in enumerate(albums, 1):
+      for key, size in __class__.COVER_SIZES.items():
+        img_url = album[key]
+        thumbnail_url = album["cover_small"]
+        results.append(DeezerCoverSourceResult(img_url,
+                                               size,
+                                               CoverImageFormat.JPEG,
+                                               thumbnail_url=thumbnail_url,
+                                               source=self,
+                                               rank=rank,
+                                               
check_metadata=CoverImageMetadata.NONE))
+
+    return results
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/tests/__init__.py 
new/sacad-2.3.0/tests/__init__.py
--- old/sacad-2.2.3/tests/__init__.py   2019-11-30 20:51:20.000000000 +0100
+++ new/sacad-2.3.0/tests/__init__.py   2020-06-08 01:13:37.000000000 +0200
@@ -148,7 +148,10 @@
     """ Check all sources return valid results with different parameters. """
     for size in range(300, 1200 + 1, 300):
       source_args = (size, 0)
-      sources = []
+      sources = [sacad.sources.LastFmCoverSource(*source_args),
+                 sacad.sources.DeezerCoverSource(*source_args),
+                 #sacad.sources.GoogleImagesWebScrapeCoverSource(*source_args),
+                 sacad.sources.AmazonDigitalCoverSource(*source_args)]
       sources.extend(sacad.sources.AmazonCdCoverSource(*source_args, tld=tld) 
for tld in sacad.sources.AmazonCdCoverSource.TLDS)
       for artist, album in zip(("Michael Jackson", "Björk"), ("Thriller", 
"Vespertine")):
         for source in sources:
@@ -158,6 +161,7 @@
             coroutine = 
sacad.CoverSourceResult.preProcessForComparison(results, size, 0)
             results = sched_and_run(coroutine, delay=0.5)
             if not (((size > 500) and isinstance(source, 
sacad.sources.LastFmCoverSource)) or
+                    ((size > 1000) and isinstance(source, 
sacad.sources.DeezerCoverSource)) or
                     (isinstance(source, sacad.sources.AmazonCdCoverSource) and
                      
(urllib.parse.urlsplit(source.base_url).netloc.rsplit(".", 1)[-1] in ("cn", 
"jp")))):
               self.assertGreaterEqual(len(results), 1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/tests/recurse_test.py 
new/sacad-2.3.0/tests/recurse_test.py
--- old/sacad-2.2.3/tests/recurse_test.py       2019-11-30 20:51:20.000000000 
+0100
+++ new/sacad-2.3.0/tests/recurse_test.py       2020-06-08 01:13:37.000000000 
+0200
@@ -13,7 +13,7 @@
 import requests
 
 import sacad.recurse as recurse
-from sacad.recurse import Metadata, Work
+from sacad.recurse import Metadata
 
 
 def download(url, filepath):
@@ -42,6 +42,10 @@
 
   @classmethod
   def setUpClass(cls):
+    #
+    # Album 1: 1 valid ogg track
+    #
+
     cls.temp_dir = tempfile.TemporaryDirectory()
     cls.album1_dir = os.path.join(cls.temp_dir.name, "album1")
     os.mkdir(cls.album1_dir)
@@ -53,6 +57,10 @@
     mf["album"] = "ALBUM1"
     mf.save()
 
+    #
+    # Album 2: 1 valid track + 1 invalid file + 1 png file
+    #
+
     cls.album2_dir = os.path.join(cls.temp_dir.name, "album2")
     os.mkdir(cls.album2_dir)
     cls.album2_filepath1 = os.path.join(cls.album2_dir, "1.dat")
@@ -65,28 +73,50 @@
     mf["album"] = "ALBUM2"
     mf.save()
 
+    open(os.path.join(cls.album2_dir, "1.png"), "wb").close()
+
+    #
+    # Album 3: 1 valid mp3 track
+    #
+
     cls.album3_dir = os.path.join(cls.temp_dir.name, "album3")
     os.mkdir(cls.album3_dir)
     url = 
r"https://www.dropbox.com/s/mtac0y8azs5hqxo/Shuffle%2520for%2520K.M.mp3?dl=1";
     cls.album3_filepath = os.path.join(cls.album3_dir, "1 track.mp3")
     download(url, cls.album3_filepath)
 
+    #
+    # Album 4: 1 valid m4a track
+    #
+
     cls.album4_dir = os.path.join(cls.temp_dir.name, "album4")
     os.mkdir(cls.album4_dir)
     url = 
"https://auphonic.com/media/audio-examples/01.auphonic-demo-unprocessed.m4a";
     cls.album4_filepath = os.path.join(cls.album4_dir, "1 track.m4a")
     download(url, cls.album4_filepath)
 
+    #
+    # Album 5: 2 valid ogg tracks with different metadata
+    #
+
     cls.album5_dir = os.path.join(cls.temp_dir.name, "album5")
     os.mkdir(cls.album5_dir)
     cls.album5_filepath1 = shutil.copy(cls.album1_filepath, cls.album5_dir)
     cls.album5_filepath2 = shutil.copy(cls.album2_filepath2, 
os.path.join(cls.album5_dir,
                                                                           
"bzz.ogg"))
 
+    #
+    # 'not album' dir: 1 invalid file
+    #
+
     cls.not_album_dir = os.path.join(cls.temp_dir.name, "not an album")
     os.mkdir(cls.not_album_dir)
     shutil.copyfile(cls.album2_filepath1, os.path.join(cls.not_album_dir, 
"a.dat"))
 
+    #
+    # 'invalid album' dir: 1 ogg track without metadata + 1 invalid file
+    #
+
     cls.invalid_album_dir = os.path.join(cls.temp_dir.name, "invalid album")
     os.mkdir(cls.invalid_album_dir)
     cls.invalid_album_filepath1 = shutil.copyfile(cls.album1_filepath,
@@ -104,11 +134,13 @@
     cls.temp_dir.cleanup()
 
   def test_analyze_lib(self):
-    with open(os.devnull, "wt") as dn, contextlib.redirect_stdout(dn):
-      for full_scan in (False, True):
-        with self.subTest(full_scan=full_scan):
-          work = recurse.analyze_lib(__class__.temp_dir.name, "a.jpg",
-                                     full_scan=full_scan)
+    for full_scan in (False, True):
+      for all_formats in (False, True):
+        with self.subTest(full_scan=full_scan, all_formats=all_formats):
+          work = recurse.analyze_lib(__class__.temp_dir.name,
+                                     "a.jpg",
+                                     full_scan=full_scan,
+                                     all_formats=all_formats)
           work.sort(key=lambda x: (x.cover_filepath, x.metadata))
           self.assertEqual(len(work), 5 + int(full_scan))
           self.assertEqual(work[0].cover_filepath,
@@ -137,30 +169,38 @@
             self.assertEqual(work[5].metadata,
                              Metadata("ARTIST2", "ALBUM2", False))
 
-          work = recurse.analyze_lib(__class__.temp_dir.name, "1.dat",
-                                     full_scan=full_scan)
+          work = recurse.analyze_lib(__class__.temp_dir.name,
+                                     "1.dat",
+                                     full_scan=full_scan,
+                                     all_formats=all_formats)
           work.sort(key=lambda x: (x.cover_filepath, x.metadata))
-          self.assertEqual(len(work), 4 + int(full_scan))
-          self.assertEqual(work[0].cover_filepath,
-                           os.path.join(__class__.album1_dir, "1.dat"))
-          self.assertEqual(work[0].metadata,
-                           Metadata("ARTIST1", "ALBUM1", False))
-          self.assertEqual(work[1].cover_filepath,
+          self.assertEqual(len(work), 4 + int(full_scan) - int(all_formats))
+          idx = 0
+          if not all_formats:
+            self.assertEqual(work[idx].cover_filepath,
+                             os.path.join(__class__.album1_dir, "1.dat"))
+            self.assertEqual(work[idx].metadata,
+                             Metadata("ARTIST1", "ALBUM1", False))
+            idx += 1
+          self.assertEqual(work[idx].cover_filepath,
                            os.path.join(__class__.album3_dir, "1.dat"))
-          self.assertEqual(work[1].metadata,
+          self.assertEqual(work[idx].metadata,
                            Metadata("jpfmband", "Paris S.F", True))
-          self.assertEqual(work[2].cover_filepath,
+          idx += 1
+          self.assertEqual(work[idx].cover_filepath,
                            os.path.join(__class__.album4_dir, "1.dat"))
-          self.assertEqual(work[2].metadata,
+          self.assertEqual(work[idx].metadata,
                            Metadata("Auphonic", "Auphonic Demonstration", 
True))
-          self.assertEqual(work[3].cover_filepath,
+          idx += 1
+          self.assertEqual(work[idx].cover_filepath,
                            os.path.join(__class__.album5_dir, "1.dat"))
-          self.assertEqual(work[3].metadata,
+          self.assertEqual(work[idx].metadata,
                            Metadata("ARTIST1", "ALBUM1", False))
+          idx += 1
           if full_scan:
-            self.assertEqual(work[4].cover_filepath,
+            self.assertEqual(work[idx].cover_filepath,
                              os.path.join(__class__.album5_dir, "1.dat"))
-            self.assertEqual(work[4].metadata,
+            self.assertEqual(work[idx].metadata,
                              Metadata("ARTIST2", "ALBUM2", False))
 
   def test_get_file_metadata(self):
@@ -209,48 +249,55 @@
     self.assertEqual(len(r), 0)
 
   def test_analyze_dir(self):
-    with open(os.devnull, "wt") as dn, contextlib.redirect_stdout(dn):
-      stats = collections.defaultdict(int)
-      r = recurse.analyze_dir(stats,
-                              __class__.album1_dir,
-                              os.listdir(__class__.album1_dir),
-                              "1.jpg")
-      self.assertIn("files", stats)
-      self.assertEqual(stats["files"], 1)
-      self.assertIn("albums", stats)
-      self.assertEqual(stats["albums"], 1)
-      self.assertIn("missing covers", stats)
-      self.assertEqual(stats["missing covers"], 1)
-      self.assertNotIn("errors", stats)
-      self.assertEqual(len(r), 1)
-      self.assertEqual(r[0].cover_filepath, os.path.join(__class__.album1_dir, 
"1.jpg"))
-      self.assertEqual(r[0].audio_filepaths, (__class__.album1_filepath,))
-      self.assertEqual(r[0].metadata, Metadata("ARTIST1", "ALBUM1", False))
+    stats = collections.defaultdict(int)
+    r = recurse.analyze_dir(stats,
+                            __class__.album1_dir,
+                            os.listdir(__class__.album1_dir),
+                            "1.jpg")
+    self.assertIn("files", stats)
+    self.assertEqual(stats["files"], 1)
+    self.assertIn("albums", stats)
+    self.assertEqual(stats["albums"], 1)
+    self.assertIn("missing covers", stats)
+    self.assertEqual(stats["missing covers"], 1)
+    self.assertNotIn("errors", stats)
+    self.assertEqual(len(r), 1)
+    self.assertEqual(r[0].cover_filepath, os.path.join(__class__.album1_dir, 
"1.jpg"))
+    self.assertEqual(r[0].audio_filepaths, (__class__.album1_filepath,))
+    self.assertEqual(r[0].metadata, Metadata("ARTIST1", "ALBUM1", False))
+
+    for all_formats in (False, True):
+      for format_ext in ("jpg", "jpeg"):
+        with self.subTest(all_formats=all_formats, format_ext=format_ext):
+          stats.clear()
+          r = recurse.analyze_dir(stats,
+                                  __class__.album2_dir,
+                                  os.listdir(__class__.album2_dir),
+                                  "1.%s" % (format_ext),
+                                  all_formats=all_formats)
+          self.assertIn("files", stats)
+          self.assertEqual(stats["files"], 3)
+          self.assertIn("albums", stats)
+          self.assertEqual(stats["albums"], 1)
+          if all_formats:
+            self.assertNotIn("missing covers", stats)
+            self.assertEqual(len(r), 0)
+          else:
+            self.assertIn("missing covers", stats)
+            self.assertEqual(stats["missing covers"], 1)
+            self.assertEqual(len(r), 1)
+            self.assertEqual(r[0].cover_filepath, 
os.path.join(__class__.album2_dir, "1.%s" % (format_ext)))
+            self.assertEqual(r[0].audio_filepaths, 
(__class__.album2_filepath2,))
+            self.assertEqual(r[0].metadata, Metadata("ARTIST2", "ALBUM2", 
False))
+          self.assertNotIn("errors", stats)
 
       stats.clear()
       r = recurse.analyze_dir(stats,
                               __class__.album2_dir,
                               os.listdir(__class__.album2_dir),
-                              "1.jpg")
-      self.assertIn("files", stats)
-      self.assertEqual(stats["files"], 2)
-      self.assertIn("albums", stats)
-      self.assertEqual(stats["albums"], 1)
-      self.assertIn("missing covers", stats)
-      self.assertEqual(stats["missing covers"], 1)
-      self.assertNotIn("errors", stats)
-      self.assertEqual(len(r), 1)
-      self.assertEqual(r[0].cover_filepath, os.path.join(__class__.album2_dir, 
"1.jpg"))
-      self.assertEqual(r[0].audio_filepaths, (__class__.album2_filepath2,))
-      self.assertEqual(r[0].metadata, Metadata("ARTIST2", "ALBUM2", False))
-      stats.clear()
-
-      r = recurse.analyze_dir(stats,
-                              __class__.album2_dir,
-                              os.listdir(__class__.album2_dir),
                               "1.dat")
       self.assertIn("files", stats)
-      self.assertEqual(stats["files"], 2)
+      self.assertEqual(stats["files"], 3)
       self.assertIn("albums", stats)
       self.assertEqual(stats["albums"], 1)
       self.assertNotIn("missing covers", stats)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sacad-2.2.3/win/Makefile new/sacad-2.3.0/win/Makefile
--- old/sacad-2.2.3/win/Makefile        2019-11-30 20:51:20.000000000 +0100
+++ new/sacad-2.3.0/win/Makefile        2020-06-08 01:13:37.000000000 +0200
@@ -1,11 +1,11 @@
 WINEARCH ?= win32
 
 # versions
-PYTHON_VERSION       := 3.6.4
-PYTHON_VERSION_DL    := 3.6.4
+PYTHON_VERSION       := 3.6.8
+PYTHON_VERSION_DL    := 3.6.8
 PYTHON_VERSION_MAJOR := $(word 1,$(subst ., ,${PYTHON_VERSION})).$(word 
2,$(subst ., ,${PYTHON_VERSION}))
 PYTHON_VERSION_SHORT := $(subst .,,${PYTHON_VERSION_MAJOR})
-CXFREEZE_VERSION     := 5.1.1
+CXFREEZE_VERSION     := 6.1
 LXML_VERSION         := 4.2.0
 BITARRAY_VERSION     := 0.8.3
 
@@ -160,11 +160,11 @@
 
 ${CXFREEZE_WHEEL-win32}:
        mkdir -p $(dir $@)
-       ${CURL} 
https://pypi.python.org/packages/3b/04/d68567b8f3265d3936e37c1ee8b3378dc189ec66b4d43650affbefcc69c5/$(notdir
 $@) > $@
+       ${CURL} 
https://files.pythonhosted.org/packages/ea/2b/076f152fdaff10a08ca4012713532b6d2606c1995ee85812d271135f90e2/$(notdir
 $@) > $@
 
 ${CXFREEZE_WHEEL-win64}:
        mkdir -p $(dir $@)
-       ${CURL} 
https://pypi.python.org/packages/49/2b/07fc4cfff671ea348b310400e4f13a596e02079afc6bd1c33150b2cf30de/$(notdir
 $@) > $@
+       ${CURL} 
https://files.pythonhosted.org/packages/50/dc/ad7111fc626a24504e468382c431698e4dcd60c944b9e9adf2187d073865/$(notdir
 $@) > $@
 
 ${LXML_WHEEL-win32}:
        mkdir -p $(dir $@)


Reply via email to