Beispiel #1
0
 def test_can_convert_android_incompatible_to_compatible_wav_file(self):
     for wav_path in self.adpcm_wavfile_paths():
         assert not wavconverter.is_android_compatible_wav(wav_path)
         converted_wav_path = wavconverter.convert_to_android_compatible_wav(
             wav_path)
         assert wavconverter.is_android_compatible_wav(converted_wav_path)
         os.remove(converted_wav_path)
    def test_can_convert_android_incompatible_to_compatible_wav_file(self):
        for wav_path in self.adpcm_wavfile_paths():
            assert not wavconverter.is_android_compatible_wav(wav_path)
            converted_wav_path = os.path.join(self._testresult_folder_path, os.path.basename(wav_path))

            wavconverter.convert_to_android_compatible_wav(wav_path, converted_wav_path)

            assert wavconverter.is_android_compatible_wav(converted_wav_path)
 def test_fail_on_missing_sox_binary(self):
     saved_path_env = os.environ[_ENV_PATH]
     os.environ[_ENV_PATH] = ""
     dummy_wav = self.adpcm_wavfile_paths()[0]
     try:
         wavconverter.is_android_compatible_wav(dummy_wav)
         self.fail("Expected exception 'EnvironmentError' not thrown")
     except EnvironmentError:
         try:
             wavconverter.convert_to_android_compatible_wav(dummy_wav, "dummy.wav")
             self.fail("Expected exception 'EnvironmentError' not thrown")
         except EnvironmentError:
             pass
         finally:
             assert not os.path.exists("dummy.wav")
     finally:
         os.environ[_ENV_PATH] = saved_path_env
 def test_fail_on_missing_sox_binary(self):
     saved_path_env = os.environ[_ENV_PATH]
     os.environ[_ENV_PATH] = ""
     dummy_wav = self.adpcm_wavfile_paths()[0]
     output_path = None
     try:
         wavconverter.is_android_compatible_wav(dummy_wav)
         self.fail("Expected exception 'EnvironmentError' not thrown")
     except EnvironmentError:
         try:
             output_path = wavconverter.convert_to_android_compatible_wav(dummy_wav)
             self.fail("Expected exception 'EnvironmentError' not thrown")
         except EnvironmentError:
             pass
         finally:
             if output_path is not None:
                 output_file_exists = os.path.exists(output_path)
                 if output_file_exists:
                     os.remove(output_path)
                 assert not output_file_exists
     finally:
         os.environ[_ENV_PATH] = saved_path_env
        def write_mediafiles(original_to_converted_catrobat_resource_file_name):
            def resource_name_for(file_path):
                return common.md5_hash(file_path) + os.path.splitext(file_path)[1]

            for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems():
                if scratch_md5_name in self.scratch_project.unused_resource_names:
                    log.info("Ignoring unused resource file: %s", src_path)
                    continue

                file_ext = os.path.splitext(scratch_md5_name)[1].lower()
                converted_file = False

                # TODO: refactor to a MediaConverter class
                if file_ext in {".png", ".svg", ".jpg", ".gif"}:
                    target_dir = images_path

                    if file_ext == ".svg":
                        # converting svg to png -> new md5 and filename
                        src_path = svgtopng.convert(src_path)
                        if not os.path.exists(src_path):
                            assert False, "Not existing: {}. Available files in directory: {}".format(src_path, os.listdir(os.path.dirname(src_path)))
                        converted_file = True

                elif file_ext in {".wav", ".mp3"}:
                    target_dir = sounds_path
                    if file_ext == ".wav":
                        if not wavconverter.is_android_compatible_wav(src_path):
                            temp_path = src_path.replace(".wav", "converted.wav")
                            wavconverter.convert_to_android_compatible_wav(src_path, temp_path)
                            src_path = temp_path
                            converted_file = True

                else:
                    assert file_ext in {".json"}, "Unknown media file extension: %s" % src_path
                    continue

                assert os.path.exists(src_path), "Not existing: {}. Available files in directory: {}".format(src_path, os.listdir(os.path.dirname(src_path)))

                # for Catrobat separate file is needed for resources which are used multiple times but with different names
                for scratch_resource_name in self.scratch_project.find_all_resource_names_for(scratch_md5_name):
                    catrobat_resource_file_name = self._catrobat_resource_file_name_for(scratch_md5_name, scratch_resource_name)
                    if converted_file:
                        original_resource_file_name = catrobat_resource_file_name
                        converted_scratch_md5_name = resource_name_for(src_path)
                        converted_resource_file_name = self._catrobat_resource_file_name_for(converted_scratch_md5_name, scratch_resource_name)
                        catrobat_resource_file_name = original_to_converted_catrobat_resource_file_name[original_resource_file_name] = converted_resource_file_name
                        assert catrobat_resource_file_name != original_resource_file_name
                    shutil.copyfile(src_path, os.path.join(target_dir, catrobat_resource_file_name))
                if converted_file:
                    os.remove(src_path)
Beispiel #6
0
 def test_fail_on_missing_sox_binary(self):
     saved_path_env = os.environ[_ENV_PATH]
     os.environ[_ENV_PATH] = ""
     dummy_wav = self.adpcm_wavfile_paths()[0]
     output_path = None
     try:
         wavconverter.is_android_compatible_wav(dummy_wav)
         self.fail("Expected exception 'EnvironmentError' not thrown")
     except EnvironmentError:
         try:
             output_path = wavconverter.convert_to_android_compatible_wav(
                 dummy_wav)
             self.fail("Expected exception 'EnvironmentError' not thrown")
         except EnvironmentError:
             pass
         finally:
             if output_path is not None:
                 output_file_exists = os.path.exists(output_path)
                 if output_file_exists:
                     os.remove(output_path)
                 assert not output_file_exists
     finally:
         os.environ[_ENV_PATH] = saved_path_env
    def convert(self, progress_bar = None):
        all_used_resources = []
        unconverted_media_resources = []
        converted_media_resources_paths = set()

        # TODO: remove this block later {
        for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems():
            if scratch_md5_name in self.scratch_project.unused_resource_names:
                log.info("Ignoring unused resource file: %s", src_path)
                if progress_bar != None:
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                continue
        # }

        for scratch_object in self.scratch_project.objects:
            project_base_path = self.scratch_project.project_base_path

            for costume_info in scratch_object.get_costumes():
                costume_file_name = costume_info[JsonKeys.COSTUME_MD5]
                costume_src_path = os.path.join(project_base_path, costume_file_name)
                file_ext = os.path.splitext(costume_file_name)[1].lower()

                if not os.path.exists(costume_src_path):
                    # media files of local projects are NOT named by their hash-value -> change name
                    costume_src_path = self.scratch_project.md5_to_resource_path_map[costume_file_name]

                assert os.path.exists(costume_src_path), "Not existing: {}".format(costume_src_path)
                assert file_ext in {".png", ".svg", ".jpg", ".gif"}, \
                       "Unsupported image file extension: %s" % costume_src_path
                ispng = file_ext == ".png"
                is_unconverted = file_ext == ".svg"

                resource_info = {
                    "scratch_md5_name": costume_file_name,
                    "src_path": costume_src_path,
                    "dest_path": self.images_path,
                    "media_type": MediaType.UNCONVERTED_SVG if is_unconverted else MediaType.IMAGE,
                    "info": costume_info
                }

                all_used_resources.append(resource_info)

                if is_unconverted:
                    unconverted_media_resources.append(resource_info)
                elif progress_bar != None and costume_src_path not in converted_media_resources_paths:
                    # update progress bar for all those media files that don't have to be converted
                    #TODO: background gets scaled too, shouldn't be the case
                    if ispng:
                        isStageCostume = scratch_object.name == "Stage"
                        self.convertPNG(isStageCostume, costume_info,costume_src_path, costume_src_path)
                    converted_media_resources_paths.add(costume_src_path)
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)


            for sound_info in scratch_object.get_sounds():
                sound_file_name = sound_info[JsonKeys.SOUND_MD5]
                sound_src_path = os.path.join(project_base_path, sound_file_name)
                file_ext = os.path.splitext(sound_file_name)[1].lower()

                if not os.path.exists(sound_src_path):
                    # media files of local projects are NOT named by their hash-value -> change name
                    sound_src_path = self.scratch_project.md5_to_resource_path_map[sound_file_name]

                assert os.path.exists(sound_src_path), "Not existing: {}".format(sound_src_path)
                assert file_ext in {".wav", ".mp3"}, "Unsupported sound file extension: %s" % sound_src_path

                is_unconverted = file_ext == ".wav" and not wavconverter.is_android_compatible_wav(sound_src_path)
                resource_info = {
                    "scratch_md5_name": sound_file_name,
                    "src_path": sound_src_path,
                    "dest_path": self.sounds_path,
                    "media_type": MediaType.UNCONVERTED_WAV if is_unconverted else MediaType.AUDIO,
                    "info": sound_info
                }

                all_used_resources.append(resource_info)

                if is_unconverted:
                    unconverted_media_resources.append(resource_info)
                elif progress_bar != None and sound_src_path not in converted_media_resources_paths:
                    # update progress bar for all those media files that don't have to be converted
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                    converted_media_resources_paths.add(sound_src_path)


        # schedule concurrent conversions (one conversion per thread)
        new_src_paths = {}
        resource_index = 0
        num_total_resources = len(unconverted_media_resources)
        reference_index = 0
        media_srces = []
        while resource_index < num_total_resources:
            num_next_resources = min(MAX_CONCURRENT_THREADS, (num_total_resources - resource_index))
            next_resources_end_index = resource_index + num_next_resources
            threads = []
            for index in range(resource_index, next_resources_end_index):
                assert index == reference_index
                reference_index += 1
                data = unconverted_media_resources[index]
                if data["src_path"] in media_srces:
                    continue
                else:
                    media_srces.append(data["src_path"])
                kwargs = {
                    "data": data,
                    "new_src_paths": new_src_paths,
                    "progress_bar": progress_bar
                }
                threads.append(_MediaResourceConverterThread(kwargs=kwargs))
            for thread in threads: thread.start()
            for thread in threads: thread.join()
            resource_index = next_resources_end_index
        assert reference_index == resource_index and reference_index == num_total_resources

        converted_media_files_to_be_removed = set()
        duplicate_filename_set = set()
        for resource_info in all_used_resources:
            # reconstruct the temporary catrobat filenames -> catrobat.media_objects_in(self.catrobat_file)
            current_filename = helpers.create_catrobat_md5_filename(resource_info["scratch_md5_name"], duplicate_filename_set)

            # check if path changed after conversion
            old_src_path = resource_info["src_path"]
            if old_src_path in new_src_paths:
                src_path = new_src_paths[old_src_path]
            else:
                src_path = old_src_path

            if resource_info["media_type"] in { MediaType.IMAGE, MediaType.UNCONVERTED_SVG }:
                costume_info = resource_info["info"]
                if "text" in costume_info:
                    text = costume_info[JsonKeys.COSTUME_TEXT]
                    x, y, width, height = costume_info[JsonKeys.COSTUME_TEXT_RECT]
                    # TODO: extract RGBA
                    # text_color = costume_info[JsonKeys.COSTUME_TEXT_COLOR]
                    font_name = "NO FONT"
                    font_style = "regular"
                    font_scaling_factor = costume_info[JsonKeys.COSTUME_RESOLUTION] if JsonKeys.COSTUME_RESOLUTION in costume_info else 1
                    if len(costume_info[JsonKeys.COSTUME_FONT_NAME].split()) == 2 :
                        [font_name, font_style] = costume_info[JsonKeys.COSTUME_FONT_NAME].split()
                    else:
                        log.warning("font JSON parameters wrong '{0}', replacing with known font '{1}'".format(costume_info[JsonKeys.COSTUME_FONT_NAME], image_processing._supported_fonts_path_mapping.keys()[0]))
                        font_scaling_factor = font_scaling_factor * 1.1 # the original font might be smaller, better scale it down than cut it off
                    if(font_name not in image_processing._supported_fonts_path_mapping):
                        log.warning("font name '{0}' unknown, replacing with known font '{1}'".format(font_name, image_processing._supported_fonts_path_mapping.keys()[0]))
                        font_name = image_processing._supported_fonts_path_mapping.keys()[0]
                        font_scaling_factor = font_scaling_factor * 1.1 # the original font might be smaller, better scale it down than cut it off
                    is_bold = font_style == "Bold"
                    is_italic = font_style == "Italic"
                    font_size = float(costume_info[JsonKeys.COSTUME_FONT_SIZE]) / float(font_scaling_factor)
                    image_file_path = src_path
                    font = image_processing.create_font(font_name, font_size, is_bold, is_italic)
                    assert font is not None
                    editable_image = image_processing.read_editable_image_from_disk(image_file_path)
                    fonty = float(y) + (height * float(font_scaling_factor) / 2.0) # I think this might not work if we rotate something outside of the picture
                    editable_image = image_processing.add_text_to_image(editable_image, text, font, Color.BLACK, float(x), float(fonty), float(width), float(height))

                    # TODO: create duplicate...
                    # TODO: move test_converter.py to converter-python-package...
                    image_processing.save_editable_image_as_png_to_disk(editable_image, image_file_path, overwrite=True)

            current_basename, _ = os.path.splitext(current_filename)
            self.file_rename_map[current_basename] = {}
            self.file_rename_map[current_basename]["src_path"] = src_path
            self.file_rename_map[current_basename]["dst_path"] = resource_info["dest_path"]
            self.file_rename_map[current_basename]["media_type"] = resource_info["media_type"]

            if resource_info["media_type"] in { MediaType.UNCONVERTED_SVG, MediaType.UNCONVERTED_WAV }:
                converted_media_files_to_be_removed.add(src_path)

        self.rename_media_files_and_copy()

        # delete converted png files -> only temporary saved
        for media_file_to_be_removed in converted_media_files_to_be_removed:
            os.remove(media_file_to_be_removed)
Beispiel #8
0
    def convert(self, progress_bar=None):
        all_used_resources = []
        unconverted_media_resources = []
        converted_media_resources_paths = set()

        # TODO: remove this block later {
        for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems(
        ):
            if scratch_md5_name in self.scratch_project.unused_resource_names:
                log.info("Ignoring unused resource file: %s", src_path)
                if progress_bar != None:
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                continue
        # }

        for scratch_object in self.scratch_project.objects:
            project_base_path = self.scratch_project.project_base_path

            for costume_info in scratch_object.get_costumes():
                costume_file_name = costume_info[JsonKeys.COSTUME_MD5]
                costume_src_path = os.path.join(project_base_path,
                                                costume_file_name)
                file_ext = os.path.splitext(costume_file_name)[1].lower()

                if not os.path.exists(costume_src_path):
                    # media files of local projects are NOT named by their hash-value -> change name
                    costume_src_path = self.scratch_project.md5_to_resource_path_map[
                        costume_file_name]

                assert os.path.exists(
                    costume_src_path), "Not existing: {}".format(
                        costume_src_path)
                assert file_ext in {".png", ".svg", ".jpg", ".gif"}, \
                       "Unsupported image file extension: %s" % costume_src_path

                is_unconverted = file_ext == ".svg"
                resource_info = {
                    "scratch_md5_name":
                    costume_file_name,
                    "src_path":
                    costume_src_path,
                    "dest_path":
                    self.images_path,
                    "media_type":
                    MediaType.UNCONVERTED_SVG
                    if is_unconverted else MediaType.IMAGE,
                    "info":
                    costume_info
                }

                all_used_resources.append(resource_info)

                if is_unconverted:
                    unconverted_media_resources.append(resource_info)
                elif progress_bar != None and costume_src_path not in converted_media_resources_paths:
                    # update progress bar for all those media files that don't have to be converted
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                    converted_media_resources_paths.add(costume_src_path)

            for sound_info in scratch_object.get_sounds():
                sound_file_name = sound_info[JsonKeys.SOUND_MD5]
                sound_src_path = os.path.join(project_base_path,
                                              sound_file_name)
                file_ext = os.path.splitext(sound_file_name)[1].lower()

                if not os.path.exists(sound_src_path):
                    # media files of local projects are NOT named by their hash-value -> change name
                    sound_src_path = self.scratch_project.md5_to_resource_path_map[
                        sound_file_name]

                assert os.path.exists(
                    sound_src_path), "Not existing: {}".format(sound_src_path)
                assert file_ext in {
                    ".wav", ".mp3"
                }, "Unsupported sound file extension: %s" % sound_src_path

                is_unconverted = file_ext == ".wav" and not wavconverter.is_android_compatible_wav(
                    sound_src_path)
                resource_info = {
                    "scratch_md5_name":
                    sound_file_name,
                    "src_path":
                    sound_src_path,
                    "dest_path":
                    self.sounds_path,
                    "media_type":
                    MediaType.UNCONVERTED_WAV
                    if is_unconverted else MediaType.AUDIO,
                    "info":
                    sound_info
                }

                all_used_resources.append(resource_info)

                if is_unconverted:
                    unconverted_media_resources.append(resource_info)
                elif progress_bar != None and sound_src_path not in converted_media_resources_paths:
                    # update progress bar for all those media files that don't have to be converted
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                    converted_media_resources_paths.add(sound_src_path)

        # schedule concurrent conversions (one conversion per thread)
        new_src_paths = {}
        resource_index = 0
        num_total_resources = len(unconverted_media_resources)
        reference_index = 0
        all_unconverted_src_media_paths = set()
        while resource_index < num_total_resources:
            num_next_resources = min(MAX_CONCURRENT_THREADS,
                                     (num_total_resources - resource_index))
            next_resources_end_index = resource_index + num_next_resources
            threads = []
            for index in range(resource_index, next_resources_end_index):
                assert index == reference_index
                reference_index += 1
                data = unconverted_media_resources[index]
                should_update_progress_bar = not data[
                    "src_path"] in all_unconverted_src_media_paths
                all_unconverted_src_media_paths.add(data["src_path"])
                kwargs = {
                    "data":
                    data,
                    "new_src_paths":
                    new_src_paths,
                    "progress_bar":
                    progress_bar if should_update_progress_bar else None
                }
                threads.append(_MediaResourceConverterThread(kwargs=kwargs))
            for thread in threads:
                thread.start()
            for thread in threads:
                thread.join()
            resource_index = next_resources_end_index
        assert reference_index == resource_index and reference_index == num_total_resources

        converted_media_files_to_be_removed = set()
        for resource_info in all_used_resources:
            scratch_md5_name = resource_info["scratch_md5_name"]

            # check if path changed after conversion
            old_src_path = resource_info["src_path"]
            if old_src_path in new_src_paths:
                src_path = new_src_paths[old_src_path]
            else:
                src_path = old_src_path

            if resource_info["media_type"] in {
                    MediaType.IMAGE, MediaType.UNCONVERTED_SVG
            }:
                costume_info = resource_info["info"]
                if "text" in costume_info:
                    text = costume_info[JsonKeys.COSTUME_TEXT]
                    x, y, width, height = costume_info[
                        JsonKeys.COSTUME_TEXT_RECT]
                    # TODO: extract RGBA
                    # text_color = costume_info[JsonKeys.COSTUME_TEXT_COLOR]
                    [font_name, font_style
                     ] = costume_info[JsonKeys.COSTUME_FONT_NAME].split()
                    is_bold = font_style == "Bold"
                    is_italic = font_style == "Italic"
                    font_size = float(costume_info[JsonKeys.COSTUME_FONT_SIZE])
                    image_file_path = src_path
                    font = image_processing.create_font(
                        font_name, font_size, is_bold, is_italic)
                    assert font is not None
                    editable_image = image_processing.read_editable_image_from_disk(
                        image_file_path)
                    editable_image = image_processing.add_text_to_image(
                        editable_image, text, font, Color.BLACK, float(x),
                        float(y), float(width), float(height))
                    # TODO: create duplicate...
                    # TODO: move test_converter.py to converter-python-package...
                    image_processing.save_editable_image_as_png_to_disk(
                        editable_image, image_file_path, overwrite=True)

            self._copy_media_file(scratch_md5_name, src_path,
                                  resource_info["dest_path"],
                                  resource_info["media_type"])

            if resource_info["media_type"] in {
                    MediaType.UNCONVERTED_SVG, MediaType.UNCONVERTED_WAV
            }:
                converted_media_files_to_be_removed.add(src_path)

        self._update_file_names_of_converted_media_files()

        for media_file_to_be_removed in converted_media_files_to_be_removed:
            os.remove(media_file_to_be_removed)
    def convert(self, progress_bar = None):
        all_used_resources = []
        unconverted_media_resources = []
        converted_media_resources_paths = set()

        # TODO: remove this block later {
        for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems():
            if scratch_md5_name in self.scratch_project.unused_resource_names:
                log.info("Ignoring unused resource file: %s", src_path)
                if progress_bar != None:
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                continue
        # }

        for scratch_object in self.scratch_project.objects:
            project_base_path = self.scratch_project.project_base_path

            for costume_info in scratch_object.get_costumes():
                costume_file_name = costume_info[JsonKeys.COSTUME_MD5]
                costume_src_path = os.path.join(project_base_path, costume_file_name)
                file_ext = os.path.splitext(costume_file_name)[1].lower()

                if not os.path.exists(costume_src_path):
                    # media files of local projects are NOT named by their hash-value -> change name
                    costume_src_path = self.scratch_project.md5_to_resource_path_map[costume_file_name]

                assert os.path.exists(costume_src_path), "Not existing: {}".format(costume_src_path)
                assert file_ext in {".png", ".svg", ".jpg", ".gif"}, \
                       "Unsupported image file extension: %s" % costume_src_path
                ispng = file_ext == ".png"
                is_unconverted = file_ext == ".svg"

                resource_info = {
                    "scratch_md5_name": costume_file_name,
                    "src_path": costume_src_path,
                    "dest_path": self.images_path,
                    "media_type": MediaType.UNCONVERTED_SVG if is_unconverted else MediaType.IMAGE,
                    "info": costume_info
                }

                all_used_resources.append(resource_info)

                if is_unconverted:
                    unconverted_media_resources.append(resource_info)
                elif progress_bar != None and costume_src_path not in converted_media_resources_paths:
                    # update progress bar for all those media files that don't have to be converted
                    #TODO: background gets scaled too, shouldn't be the case
                    if ispng:
                        isStageCostume = scratch_object.name == "Stage"
                        self.convertPNG(isStageCostume, costume_info,costume_src_path, costume_src_path)
                    converted_media_resources_paths.add(costume_src_path)
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)


            for sound_info in scratch_object.get_sounds():
                sound_file_name = sound_info[JsonKeys.SOUND_MD5]
                sound_src_path = os.path.join(project_base_path, sound_file_name)
                file_ext = os.path.splitext(sound_file_name)[1].lower()

                if not os.path.exists(sound_src_path):
                    # media files of local projects are NOT named by their hash-value -> change name
                    sound_src_path = self.scratch_project.md5_to_resource_path_map[sound_file_name]

                assert os.path.exists(sound_src_path), "Not existing: {}".format(sound_src_path)
                assert file_ext in {".wav", ".mp3"}, "Unsupported sound file extension: %s" % sound_src_path

                is_unconverted = file_ext == ".wav" and not wavconverter.is_android_compatible_wav(sound_src_path)
                resource_info = {
                    "scratch_md5_name": sound_file_name,
                    "src_path": sound_src_path,
                    "dest_path": self.sounds_path,
                    "media_type": MediaType.UNCONVERTED_WAV if is_unconverted else MediaType.AUDIO,
                    "info": sound_info
                }

                all_used_resources.append(resource_info)

                if is_unconverted:
                    unconverted_media_resources.append(resource_info)
                elif progress_bar != None and sound_src_path not in converted_media_resources_paths:
                    # update progress bar for all those media files that don't have to be converted
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                    converted_media_resources_paths.add(sound_src_path)


        # schedule concurrent conversions (one conversion per thread)
        new_src_paths = {}
        resource_index = 0
        num_total_resources = len(unconverted_media_resources)
        reference_index = 0
        media_srces = []
        while resource_index < num_total_resources:
            num_next_resources = min(MAX_CONCURRENT_THREADS, (num_total_resources - resource_index))
            next_resources_end_index = resource_index + num_next_resources
            threads = []
            for index in range(resource_index, next_resources_end_index):
                assert index == reference_index
                reference_index += 1
                data = unconverted_media_resources[index]
                if data["src_path"] in media_srces:
                    continue
                else:
                    media_srces.append(data["src_path"])
                kwargs = {
                    "data": data,
                    "new_src_paths": new_src_paths,
                    "progress_bar": progress_bar
                }
                threads.append(_MediaResourceConverterThread(kwargs=kwargs))
            for thread in threads: thread.start()
            for thread in threads: thread.join()
            resource_index = next_resources_end_index
        assert reference_index == resource_index and reference_index == num_total_resources

        converted_media_files_to_be_removed = set()
        for resource_info in all_used_resources:
            scratch_md5_name = resource_info["scratch_md5_name"]

            # check if path changed after conversion
            old_src_path = resource_info["src_path"]
            if old_src_path in new_src_paths:
                src_path = new_src_paths[old_src_path]
            else:
                src_path = old_src_path

            if resource_info["media_type"] in { MediaType.IMAGE, MediaType.UNCONVERTED_SVG }:
                costume_info = resource_info["info"]
                if "text" in costume_info:
                    text = costume_info[JsonKeys.COSTUME_TEXT]
                    x, y, width, height = costume_info[JsonKeys.COSTUME_TEXT_RECT]
                    # TODO: extract RGBA
                    # text_color = costume_info[JsonKeys.COSTUME_TEXT_COLOR]
                    font_name = "NO FONT"
                    font_style = "regular"
                    font_scaling_factor = costume_info[JsonKeys.COSTUME_RESOLUTION] if JsonKeys.COSTUME_RESOLUTION in costume_info else 1
                    if len(costume_info[JsonKeys.COSTUME_FONT_NAME].split()) == 2 :
                        [font_name, font_style] = costume_info[JsonKeys.COSTUME_FONT_NAME].split()
                    else:
                        log.warning("font JSON parameters wrong '{0}', replacing with known font '{1}'".format(costume_info[JsonKeys.COSTUME_FONT_NAME], image_processing._supported_fonts_path_mapping.keys()[0]))
                        font_scaling_factor = font_scaling_factor * 1.1 # the original font might be smaller, better scale it down than cut it off
                    if(font_name not in image_processing._supported_fonts_path_mapping):
                        log.warning("font name '{0}' unknown, replacing with known font '{1}'".format(font_name, image_processing._supported_fonts_path_mapping.keys()[0]))
                        font_name = image_processing._supported_fonts_path_mapping.keys()[0]
                        font_scaling_factor = font_scaling_factor * 1.1 # the original font might be smaller, better scale it down than cut it off
                    is_bold = font_style == "Bold"
                    is_italic = font_style == "Italic"
                    font_size = float(costume_info[JsonKeys.COSTUME_FONT_SIZE]) / float(font_scaling_factor)
                    image_file_path = src_path
                    font = image_processing.create_font(font_name, font_size, is_bold, is_italic)
                    assert font is not None
                    editable_image = image_processing.read_editable_image_from_disk(image_file_path)
                    fonty = float(y) + (height * float(font_scaling_factor) / 2.0) # I think this might not work if we rotate something outside of the picture
                    editable_image = image_processing.add_text_to_image(editable_image, text, font, Color.BLACK, float(x), float(fonty), float(width), float(height))

                    # TODO: create duplicate...
                    # TODO: move test_converter.py to converter-python-package...
                    image_processing.save_editable_image_as_png_to_disk(editable_image, image_file_path, overwrite=True)

            self._copy_media_file(scratch_md5_name, src_path, resource_info["dest_path"],
                                  resource_info["media_type"])

            if resource_info["media_type"] in { MediaType.UNCONVERTED_SVG, MediaType.UNCONVERTED_WAV }:
                converted_media_files_to_be_removed.add(src_path)

        self._update_file_names_of_converted_media_files()

        for media_file_to_be_removed in converted_media_files_to_be_removed:
            os.remove(media_file_to_be_removed)
 def test_can_convert_android_incompatible_to_compatible_wav_file(self):
     for wav_path in self.adpcm_wavfile_paths():
         assert not wavconverter.is_android_compatible_wav(wav_path)
         converted_wav_path = wavconverter.convert_to_android_compatible_wav(wav_path)
         assert wavconverter.is_android_compatible_wav(converted_wav_path)
         os.remove(converted_wav_path)
 def test_can_detect_android_compatible_wav_file(self):
     for wav_path in self.pcm_wavfile_paths():
         assert wavconverter.is_android_compatible_wav(wav_path)
Beispiel #12
0
 def test_can_detect_android_compatible_wav_file(self):
     for wav_path in self.pcm_wavfile_paths():
         assert wavconverter.is_android_compatible_wav(wav_path)
    def convert(self, progress_bar=None):
        all_used_resources = []
        unconverted_media_resources = []
        converted_media_resources_paths = set()

        # TODO: remove this block later {
        for scratch_md5_name, src_path in self.scratch_project.md5_to_resource_path_map.iteritems():
            if scratch_md5_name in self.scratch_project.unused_resource_names:
                log.info("Ignoring unused resource file: %s", src_path)
                if progress_bar != None:
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                continue
        # }

        for scratch_object in self.scratch_project.objects:
            project_base_path = self.scratch_project.project_base_path

            for costume_info in scratch_object.get_costumes():
                costume_file_name = costume_info[JsonKeys.COSTUME_MD5]
                costume_src_path = os.path.join(project_base_path, costume_file_name)
                file_ext = os.path.splitext(costume_file_name)[1].lower()

                if not os.path.exists(costume_src_path):
                    # media files of local projects are NOT named by their hash-value -> change name
                    costume_src_path = self.scratch_project.md5_to_resource_path_map[costume_file_name]

                assert os.path.exists(costume_src_path), "Not existing: {}".format(costume_src_path)
                assert file_ext in {".png", ".svg", ".jpg", ".gif"}, (
                    "Unsupported image file extension: %s" % costume_src_path
                )

                is_unconverted = file_ext == ".svg"
                resource_info = {
                    "scratch_md5_name": costume_file_name,
                    "src_path": costume_src_path,
                    "dest_path": self.images_path,
                    "media_type": MediaType.UNCONVERTED_SVG if is_unconverted else MediaType.IMAGE,
                    "info": costume_info,
                }

                all_used_resources.append(resource_info)

                if is_unconverted:
                    unconverted_media_resources.append(resource_info)
                elif progress_bar != None and costume_src_path not in converted_media_resources_paths:
                    # update progress bar for all those media files that don't have to be converted
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                    converted_media_resources_paths.add(costume_src_path)

            for sound_info in scratch_object.get_sounds():
                sound_file_name = sound_info[JsonKeys.SOUND_MD5]
                sound_src_path = os.path.join(project_base_path, sound_file_name)
                file_ext = os.path.splitext(sound_file_name)[1].lower()

                if not os.path.exists(sound_src_path):
                    # media files of local projects are NOT named by their hash-value -> change name
                    sound_src_path = self.scratch_project.md5_to_resource_path_map[sound_file_name]

                assert os.path.exists(sound_src_path), "Not existing: {}".format(sound_src_path)
                assert file_ext in {".wav", ".mp3"}, "Unsupported sound file extension: %s" % sound_src_path

                is_unconverted = file_ext == ".wav" and not wavconverter.is_android_compatible_wav(sound_src_path)
                resource_info = {
                    "scratch_md5_name": sound_file_name,
                    "src_path": sound_src_path,
                    "dest_path": self.sounds_path,
                    "media_type": MediaType.UNCONVERTED_WAV if is_unconverted else MediaType.AUDIO,
                    "info": sound_info,
                }

                all_used_resources.append(resource_info)

                if is_unconverted:
                    unconverted_media_resources.append(resource_info)
                elif progress_bar != None and sound_src_path not in converted_media_resources_paths:
                    # update progress bar for all those media files that don't have to be converted
                    progress_bar.update(ProgressType.CONVERT_MEDIA_FILE)
                    converted_media_resources_paths.add(sound_src_path)

        # schedule concurrent conversions (one conversion per thread)
        new_src_paths = {}
        resource_index = 0
        num_total_resources = len(unconverted_media_resources)
        reference_index = 0
        all_unconverted_src_media_paths = set()
        while resource_index < num_total_resources:
            num_next_resources = min(MAX_CONCURRENT_THREADS, (num_total_resources - resource_index))
            next_resources_end_index = resource_index + num_next_resources
            threads = []
            for index in range(resource_index, next_resources_end_index):
                assert index == reference_index
                reference_index += 1
                data = unconverted_media_resources[index]
                should_update_progress_bar = not data["src_path"] in all_unconverted_src_media_paths
                all_unconverted_src_media_paths.add(data["src_path"])
                kwargs = {
                    "data": data,
                    "new_src_paths": new_src_paths,
                    "progress_bar": progress_bar if should_update_progress_bar else None,
                }
                threads.append(_MediaResourceConverterThread(kwargs=kwargs))
            for thread in threads:
                thread.start()
            for thread in threads:
                thread.join()
            resource_index = next_resources_end_index
        assert reference_index == resource_index and reference_index == num_total_resources

        converted_media_files_to_be_removed = set()
        for resource_info in all_used_resources:
            scratch_md5_name = resource_info["scratch_md5_name"]

            # check if path changed after conversion
            old_src_path = resource_info["src_path"]
            if old_src_path in new_src_paths:
                src_path = new_src_paths[old_src_path]
            else:
                src_path = old_src_path

            if resource_info["media_type"] in {MediaType.IMAGE, MediaType.UNCONVERTED_SVG}:
                costume_info = resource_info["info"]
                if "text" in costume_info:
                    text = costume_info[JsonKeys.COSTUME_TEXT]
                    x, y, width, height = costume_info[JsonKeys.COSTUME_TEXT_RECT]
                    # TODO: extract RGBA
                    # text_color = costume_info[JsonKeys.COSTUME_TEXT_COLOR]
                    [font_name, font_style] = costume_info[JsonKeys.COSTUME_FONT_NAME].split()
                    is_bold = font_style == "Bold"
                    is_italic = font_style == "Italic"
                    font_size = float(costume_info[JsonKeys.COSTUME_FONT_SIZE])
                    image_file_path = src_path
                    font = image_processing.create_font(font_name, font_size, is_bold, is_italic)
                    assert font is not None
                    editable_image = image_processing.read_editable_image_from_disk(image_file_path)
                    editable_image = image_processing.add_text_to_image(
                        editable_image, text, font, Color.BLACK, float(x), float(y), float(width), float(height)
                    )
                    # TODO: create duplicate...
                    # TODO: move test_converter.py to converter-python-package...
                    image_processing.save_editable_image_as_png_to_disk(editable_image, image_file_path, overwrite=True)

            self._copy_media_file(scratch_md5_name, src_path, resource_info["dest_path"], resource_info["media_type"])

            if resource_info["media_type"] in {MediaType.UNCONVERTED_SVG, MediaType.UNCONVERTED_WAV}:
                converted_media_files_to_be_removed.add(src_path)

        self._update_file_names_of_converted_media_files()

        for media_file_to_be_removed in converted_media_files_to_be_removed:
            os.remove(media_file_to_be_removed)