Beispiel #1
0
 def test_getMasterOfPuppetsCover(self):
     """ Search and download cover for 'Master of Puppets' with different parameters. """
     for format in sacad.cover.CoverImageFormat:
         for size in (300, 600, 1200):
             for size_tolerance in (0, 25, 50):
                 with sacad.mkstemp_ctx.mkstemp(
                         prefix="sacad_test_",
                         suffix=".%s" %
                     (format.name.lower())) as tmp_filepath:
                     sacad.search_and_download("Master of Puppets",
                                               "Metallica", format, size,
                                               size_tolerance, (), False,
                                               tmp_filepath)
                     out_format, out_width, out_height = __class__.getImgInfo(
                         tmp_filepath)
                     self.assertEqual(out_format, format)
                     self.assertLessEqual(
                         out_width,
                         size * (100 + size_tolerance) / 100)
                     self.assertGreaterEqual(
                         out_width,
                         size * (100 - size_tolerance) / 100)
                     self.assertLessEqual(
                         out_height,
                         size * (100 + size_tolerance) / 100)
                     self.assertGreaterEqual(
                         out_height,
                         size * (100 - size_tolerance) / 100)
Beispiel #2
0
 def test_getMasterOfPuppetsCover(self):
   """ Search and download cover for 'Master of Puppets' with different parameters. """
   async_loop = asyncio.get_event_loop()
   for format in sacad.cover.CoverImageFormat:
     for size in (300, 600, 1200):
       for size_tolerance in (0, 25, 50):
         with self.subTest(format=format, size=size, size_tolerance=size_tolerance):
           with sacad.mkstemp_ctx.mkstemp(prefix="sacad_test_",
                                          suffix=".%s" % (format.name.lower())) as tmp_filepath:
             coroutine = sacad.search_and_download("Master of Puppets",
                                                   "Metallica",
                                                   format,
                                                   size,
                                                   tmp_filepath,
                                                   size_tolerance_prct=size_tolerance,
                                                   amazon_tlds=(),
                                                   no_lq_sources=False,
                                                   async_loop=async_loop)
             sched_and_run(coroutine, async_loop, delay=0.5)
             out_format, out_width, out_height = __class__.getImgInfo(tmp_filepath)
             self.assertEqual(out_format, format)
             self.assertLessEqual(out_width, size * (100 + size_tolerance) / 100)
             self.assertGreaterEqual(out_width, size * (100 - size_tolerance) / 100)
             self.assertLessEqual(out_height, size * (100 + size_tolerance) / 100)
             self.assertGreaterEqual(out_height, size * (100 - size_tolerance) / 100)
    def getCoverArt(self, artist, album):

        # search and download
        threadID = str(threading.get_ident())
        print('Thread ID: ', threadID)

        coroutine = sacad.search_and_download(album,
                                              artist,
                                              SUPPORTED_IMG_FORMATS["jpg"],
                                              400,
                                              "/tmp/cover.jpg",
                                              size_tolerance_prct=25,
                                              amazon_tlds=["co.uk"],
                                              no_lq_sources=False,
                                              async_loop=self.async_loop)
        future = asyncio.ensure_future(coroutine, loop=self.async_loop)
        self.async_loop.run_until_complete(future)
Beispiel #4
0
 def test_getMasterOfPuppetsCover(self):
     """ Search and download cover for 'Master of Puppets' with different parameters. """
     async_loop = asyncio.get_event_loop()
     for format in sacad.cover.CoverImageFormat:
         for size in (300, 600, 1200):
             for size_tolerance in (0, 25, 50):
                 with self.subTest(format=format,
                                   size=size,
                                   size_tolerance=size_tolerance):
                     with sacad.mkstemp_ctx.mkstemp(
                             prefix="sacad_test_",
                             suffix=".%s" %
                         (format.name.lower())) as tmp_filepath:
                         coroutine = sacad.search_and_download(
                             "Master of Puppets",
                             "Metallica",
                             format,
                             size,
                             tmp_filepath,
                             size_tolerance_prct=size_tolerance,
                             amazon_tlds=(),
                             no_lq_sources=False,
                             async_loop=async_loop)
                         sched_and_run(coroutine, async_loop, delay=0.5)
                         out_format, out_width, out_height = __class__.getImgInfo(
                             tmp_filepath)
                         self.assertEqual(out_format, format)
                         self.assertLessEqual(
                             out_width,
                             size * (100 + size_tolerance) / 100)
                         self.assertGreaterEqual(
                             out_width,
                             size * (100 - size_tolerance) / 100)
                         self.assertLessEqual(
                             out_height,
                             size * (100 + size_tolerance) / 100)
                         self.assertGreaterEqual(
                             out_height,
                             size * (100 - size_tolerance) / 100)
Beispiel #5
0
def downloadAlbumArt(album: str, artist: str, is_single: bool = False):
    try:
        if is_single:
            fileName = f'{slugify(artist)} - {slugify(album)} - Single'
        else:
            fileName = f'{slugify(artist)} - {slugify(album)}'
        success = asyncio.get_event_loop().run_until_complete(
            sacad.search_and_download(
                album,  # Album name
                artist,  # Artist name
                "png",  # File format, or None if you don't care
                1024,  # Preferred album size
                path.expanduser(
                    f'~/Music/Albums/{fileName}.png'),  # Output path
                size_tolerance_prct=25,
                is_single=is_single))
        return success
    except Exception as e:
        print(
            f'ERROR! findAlbumArt.py - downloadAlbumArt. Passed in album: {album}, artist: {artist}. Error: {e}'
        )
        return False
Beispiel #6
0
	def fetch_media_art(self):
		if self.art_path is not None:
			return self.art_path

		if (self.meta["Artist"] is None) or (self.meta["Album"] is None):
			return None

		artdir = "artlib"

		path_concat = os.path.join("static", artdir)
		if not os.path.isdir(path_concat):
			os.makedirs(path_concat)

		filename = self.meta["Artist"] + "-" + self.meta["Album"]
		filename = unicode(unicodedata.normalize('NFKD', filename).encode('ascii', 'ignore'))
		filename = unicode(re.sub('[^\w\s-]', '', filename).strip().lower())
		filename = unicode(re.sub('[-\s]+', '-', filename)) + ".jpg"	
		path_concat = os.path.join(path_concat, filename)
		
		if os.path.isfile(path_concat):
			self.art_path = os.path.join(artdir, filename)
			return self.art_path

		if sacad.search_and_download(
				self.meta["Artist"], 
				self.meta["Album"], 
				sacad.cover.CoverImageFormat.JPEG,
				600,
				25,
				[],
				False,
				path_concat):
			self.art_path = os.path.join(artdir, filename)
			return self.art_path
		else:
			return None
Beispiel #7
0
def get_covers(work, args):
    """ Get missing covers. """
    with contextlib.ExitStack() as cm:

        if args.filename == EMBEDDED_ALBUM_ART_SYMBOL:
            tmp_prefix = "%s_" % (os.path.splitext(
                os.path.basename(inspect.getfile(inspect.currentframe())))[0])
            tmp_dir = cm.enter_context(
                tempfile.TemporaryDirectory(prefix=tmp_prefix))

        # setup progress report
        stats = collections.OrderedDict(
            ((k, 0) for k in ("ok", "errors", "no result found")))
        errors = []
        not_found = []
        with tqdm.tqdm(total=len(work),
                       miniters=1,
                       desc="Searching covers",
                       unit=" covers",
                       postfix=stats) as progress:

            def update_progress(future):
                path, cover_filepath, artist, album = futures[future]
                try:
                    status = future.result()
                except Exception as exception:
                    stats["errors"] += 1
                    errors.append((path, artist, album, exception))
                else:
                    if status:
                        if args.filename == EMBEDDED_ALBUM_ART_SYMBOL:
                            try:
                                embed_album_art(cover_filepath, path)
                            except Exception as exception:
                                stats["errors"] += 1
                                errors.append((path, artist, album, exception))
                            else:
                                stats["ok"] += 1
                            finally:
                                os.remove(cover_filepath)
                        else:
                            stats["ok"] += 1
                    else:
                        stats["no result found"] += 1
                        not_found.append((path, artist, album))
                progress.set_postfix(stats)
                progress.update(1)

            # post work
            async_loop = asyncio.get_event_loop()
            i = 0
            # default event loop on Windows has a 512 fd limit, see https://docs.python.org/3/library/asyncio-eventloops.html#windows
            # also on Linux default max open fd limit is 1024 (ulimit -n)
            # 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
            for work_chunk in ichunk(work.items(), work_chunk_length):
                futures = {}
                for i, (path, (artist, album)) in enumerate(work_chunk, i):
                    if args.filename == EMBEDDED_ALBUM_ART_SYMBOL:
                        cover_filepath = os.path.join(
                            tmp_dir, "%00u.%s" % (i, args.format.name.lower()))
                    else:
                        cover_filepath = os.path.join(path, args.filename)
                    coroutine = sacad.search_and_download(
                        album,
                        artist,
                        args.format,
                        args.size,
                        cover_filepath,
                        size_tolerance_prct=args.size_tolerance_prct,
                        amazon_tlds=args.amazon_tlds,
                        no_lq_sources=args.no_lq_sources,
                        async_loop=async_loop)
                    future = asyncio.ensure_future(coroutine, loop=async_loop)
                    futures[future] = (path, cover_filepath, artist, album)

                for future in futures:
                    future.add_done_callback(update_progress)

                # wait for end of work
                root_future = asyncio.gather(*futures.keys(), loop=async_loop)
                async_loop.run_until_complete(root_future)

    # report accumulated errors
    for path, artist, album in not_found:
        print("Unable to find cover for '%s' by '%s' from '%s'" %
              (album, artist, path))
    for path, artist, album, exception in errors:
        print(
            "Error occured while handling cover for '%s' by '%s' from '%s': %s %s"
            %
            (album, artist, path, exception.__class__.__qualname__, exception))
    if errors:
        print("Please report this at https://github.com/desbma/sacad/issues")
Beispiel #8
0
def get_covers(work, args):
    """ Get missing covers. """
    with contextlib.ExitStack() as cm:

        if args.cover_pattern == EMBEDDED_ALBUM_ART_SYMBOL:
            tmp_prefix = "%s_" % (os.path.splitext(
                os.path.basename(inspect.getfile(inspect.currentframe())))[0])
            tmp_dir = cm.enter_context(
                tempfile.TemporaryDirectory(prefix=tmp_prefix))

        # setup progress report
        stats = collections.OrderedDict(
            ((k, 0) for k in ("ok", "errors", "no result found")))
        progress = cm.enter_context(
            tqdm.tqdm(total=len(work),
                      miniters=1,
                      desc="Searching covers",
                      unit="cover",
                      postfix=stats))
        cm.enter_context(tqdm_logging.redirect_logging(progress))

        def post_download(future):
            work = futures[future]
            try:
                status = future.result()
            except Exception as exception:
                stats["errors"] += 1
                logging.getLogger("sacad_r").error(
                    "Error occured while searching %s: "
                    "%s %s" %
                    (work, exception.__class__.__qualname__, exception))
            else:
                if status:
                    if work.cover_filepath == EMBEDDED_ALBUM_ART_SYMBOL:
                        try:
                            embed_album_art(work.tmp_cover_filepath,
                                            work.audio_filepaths)
                        except Exception as exception:
                            stats["errors"] += 1
                            logging.getLogger("sacad_r").error(
                                "Error occured while embedding %s: "
                                "%s %s" %
                                (work, exception.__class__.__qualname__,
                                 exception))
                        else:
                            stats["ok"] += 1
                        finally:
                            os.remove(work.tmp_cover_filepath)
                    else:
                        stats["ok"] += 1
                else:
                    stats["no result found"] += 1
                    logging.getLogger("sacad_r").warning("Unable to find %s" %
                                                         (work))

            progress.set_postfix(stats, refresh=False)
            progress.update(1)

        # post work
        i = 0
        # default event loop on Windows has a 512 fd limit, see https://docs.python.org/3/library/asyncio-eventloops.html#windows
        # also on Linux default max open fd limit is 1024 (ulimit -n)
        # 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 = 4 if sys.platform.startswith("win") else 12
        for work_chunk in ichunk(work, work_chunk_length):
            futures = {}
            for i, cur_work in enumerate(work_chunk, i):
                if cur_work.cover_filepath == EMBEDDED_ALBUM_ART_SYMBOL:
                    cover_filepath = os.path.join(
                        tmp_dir, "%00u.%s" % (i, args.format.name.lower()))
                    cur_work.tmp_cover_filepath = cover_filepath
                else:
                    cover_filepath = cur_work.cover_filepath
                    os.makedirs(os.path.dirname(cover_filepath), exist_ok=True)
                coroutine = sacad.search_and_download(
                    cur_work.metadata.album,
                    cur_work.metadata.artist,
                    args.format,
                    args.size,
                    cover_filepath,
                    size_tolerance_prct=args.size_tolerance_prct,
                    amazon_tlds=args.amazon_tlds,
                    no_lq_sources=args.no_lq_sources,
                    preserve_format=args.preserve_format)
                future = asyncio.ensure_future(coroutine)
                futures[future] = cur_work

            for future in futures:
                future.add_done_callback(post_download)

            # wait for end of work
            root_future = asyncio.gather(*futures.keys())
            asyncio.get_event_loop().run_until_complete(root_future)
Beispiel #9
0
def get_covers(work, args):
  """ Get missing covers. """
  with contextlib.ExitStack() as cm:

    if args.filename == EMBEDDED_ALBUM_ART_SYMBOL:
      tmp_prefix = "%s_" % (os.path.splitext(os.path.basename(inspect.getfile(inspect.currentframe())))[0])
      tmp_dir = cm.enter_context(tempfile.TemporaryDirectory(prefix=tmp_prefix))

    # setup progress report
    stats = collections.OrderedDict(((k, 0) for k in("ok", "errors", "no result found")))
    progress = cm.enter_context(tqdm.tqdm(total=len(work),
                                          miniters=1,
                                          desc="Searching covers",
                                          unit="cover",
                                          postfix=stats))
    cm.enter_context(tqdm_logging.redirect_logging(progress))

    def update_progress(future):
      path, cover_filepath, artist, album = futures[future]
      try:
        status = future.result()
      except Exception as exception:
        stats["errors"] += 1
        logging.getLogger("sacad_r").error("Error occured while searching cover for "
                                           "'%s' by '%s' from '%s': %s %s" % (album,
                                                                              artist,
                                                                              path,
                                                                              exception.__class__.__qualname__,
                                                                              exception))
      else:
        if status:
          if args.filename == EMBEDDED_ALBUM_ART_SYMBOL:
            try:
              embed_album_art(cover_filepath, path)
            except Exception as exception:
              stats["errors"] += 1
              logging.getLogger("sacad_r").error("Error occured while embedding cover for "
                                                 "'%s' by '%s' from '%s': %s %s" % (album,
                                                                                    artist,
                                                                                    path,
                                                                                    exception.__class__.__qualname__,
                                                                                    exception))
            else:
              stats["ok"] += 1
            finally:
              os.remove(cover_filepath)
          else:
            stats["ok"] += 1
        else:
          stats["no result found"] += 1
          logging.getLogger("sacad_r").warning("Unable to find cover for '%s' by '%s' from '%s'" % (album, artist, path))
      progress.set_postfix(stats, refresh=False)
      progress.update(1)

    # post work
    async_loop = asyncio.get_event_loop()
    i = 0
    # default event loop on Windows has a 512 fd limit, see https://docs.python.org/3/library/asyncio-eventloops.html#windows
    # also on Linux default max open fd limit is 1024 (ulimit -n)
    # 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
    for work_chunk in ichunk(work.items(), work_chunk_length):
      futures = {}
      for i, (path, (artist, album)) in enumerate(work_chunk, i):
        if args.filename == EMBEDDED_ALBUM_ART_SYMBOL:
          cover_filepath = os.path.join(tmp_dir, "%00u.%s" % (i, args.format.name.lower()))
        else:
          cover_filepath = os.path.join(path, args.filename)
        coroutine = sacad.search_and_download(album,
                                              artist,
                                              args.format,
                                              args.size,
                                              cover_filepath,
                                              size_tolerance_prct=args.size_tolerance_prct,
                                              amazon_tlds=args.amazon_tlds,
                                              no_lq_sources=args.no_lq_sources,
                                              async_loop=async_loop)
        future = asyncio.ensure_future(coroutine, loop=async_loop)
        futures[future] = (path, cover_filepath, artist, album)

      for future in futures:
        future.add_done_callback(update_progress)

      # wait for end of work
      root_future = asyncio.gather(*futures.keys(), loop=async_loop)
      async_loop.run_until_complete(root_future)