Mercurial > hg > pub > prymula > scripts
view cover2musc/cover2music.py @ 14:4770f6dca839
bing4desktop-0.240206-3.1
author | prymula <prymula76@outlook.com> |
---|---|
date | Tue, 06 Feb 2024 18:07:17 +0100 |
parents | dcd610585610 |
children |
line wrap: on
line source
#!/usr/bin/env python3 # # cover2music - appka do przypisywania obrazków płyt plikom ogg, oga, opus oraz mp3 # program: Przemysław R. Pietraszczyk # rozpoczęto: 26-04-2021 # licencja: GPL # napisano w edytorze: Atom import gi, os gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GdkPixbuf, GLib import base64 from mutagen.mp3 import MP3 from mutagen.id3 import ID3, APIC, error from mutagen.oggvorbis import OggVorbis from mutagen.oggflac import OggFLAC from mutagen.oggopus import OggOpus from mutagen.flac import Picture import mutagen file_snd_selected=[] file_img_selected="" #snd_selected=[] #COVER ="cover2music.png" COVER ="/usr/share/pixmaps/cover2music.png" snd_extension="ogg" img_extension="png" # dla przykladu UI_INFO = """ <ui> <menubar name='MenuBar'> <menu action='FileMenu'> <menu action='FileLoad'> <menuitem action='LoadImg' /> <menuitem action='LoadOgg' /> </menu> <menu action='FileDelete'> <menuitem action='RemoveCover' /> </menu> <separator /> <menuitem action='FileQuit' /> </menu> <menu action='ChoicesMenu'> <menuitem action='ChoiceTree'/> </menu> <menu action='InfoMenu'> <menuitem action='Help'/> <menuitem action='About'/> </menu> </menubar> </ui> """ class FileChooserIMG(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="Wybór plików graficznych") global file_img_selected dialog = Gtk.FileChooserDialog(title="Please choose a file", parent=self, action=Gtk.FileChooserAction.OPEN) dialog.add_buttons( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK, ) self.add_filters(dialog) response = dialog.run() if response == Gtk.ResponseType.OK: print("Open clicked") print("File selected: " + dialog.get_filename()) file_img_selected=dialog.get_filename() elif response == Gtk.ResponseType.CANCEL: print("Cancel clicked") dialog.destroy() def add_filters(self, dialog): filter_jpeg = Gtk.FileFilter() filter_jpeg.set_name("Pliki JPEG") filter_jpeg.add_mime_type("image/jpeg") dialog.add_filter(filter_jpeg) filter_png = Gtk.FileFilter() filter_png.set_name("Pliki PNG") filter_png.add_mime_type("image/png") dialog.add_filter(filter_png) class FileChooserOgg(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="Wybór plików dzwiękowych") global file_snd_selected dialog = Gtk.FileChooserDialog(title="Please choose a file", parent=self, action=Gtk.FileChooserAction.OPEN) dialog.add_buttons( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK, ) self.add_filters(dialog) dialog.set_select_multiple(True) response = dialog.run() if response == Gtk.ResponseType.OK: print("Open clicked") file_snd_selected=dialog.get_filenames() elif response == Gtk.ResponseType.CANCEL: print("Cancel clicked") dialog.destroy() def add_filters(self, dialog): filter_ogg = Gtk.FileFilter() filter_ogg.set_name("Pliki OGG") filter_ogg.add_mime_type("audio/ogg") dialog.add_filter(filter_ogg) filter_mp3 = Gtk.FileFilter() filter_mp3.set_name("Pliki MP3") filter_mp3.add_mime_type("audio/mp3") dialog.add_filter(filter_mp3) class DialogDeleteProgress(Gtk.Dialog): def __init__(self,parent): Gtk.Dialog.__init__(self, title="Postęp", transient_for=parent, flags=0) self.current_snd=0 self.props.border_width = 20 #self.add_buttons( # Gtk.STOCK_OK, Gtk.ResponseType.OK #) self.progressbar = Gtk.ProgressBar() self.set_default_size(150, 100) box = self.get_content_area() label = Gtk.Label(label="Usuwam okladki z plików\n") box.add(label) box.add(self.progressbar) self.divider_next=1/len(file_snd_selected) self.timeout_id = GLib.timeout_add(300, self.on_timeout, len(file_snd_selected)) self.activity_mode = False self.show_all() def on_timeout(self, user_data): #jesli przepytano wszyskie pliki wówczas koniec if self.current_snd == user_data: self.add_buttons( Gtk.STOCK_OK, Gtk.ResponseType.OK ) self.show_all() return False if self.activity_mode: self.progressbar.pulse() else: new_value = self.progressbar.get_fraction() + self.divider_next #if new_value > 1: # new_value = 0 self.progressbar.set_fraction(new_value) snd=file_snd_selected[self.current_snd] self.current_snd = self.current_snd +1 print ("file selected: ",snd) extension = os.path.splitext(snd) snd_extension=extension[1].replace(".", "") print ("Rozszerzenie pliku dzwiekowego: ", snd_extension) type=self.check_file_type(snd) if type == "mp3": self.remove_cover_mp3_generic(snd) elif type == "ogg": self.remove_cover_ogg_generic(snd) elif type == "oga": self.remove_cover_oga_generic(snd) elif type == "opus": self.remove_cover_opus_generic(snd) # As this is a timeout function, return True so that it # continues to get called return True def check_file_type(self,snd): magic_numbers = {'ID3v2': bytes([0x49, 0x44, 0x33]), 'ID3v1': bytes([0xFF, 0xF2]), 'ID3': bytes([0xFF, 0xF3]), 'withoutID': bytes([0xFF, 0xFB]), 'ogg': bytes([0x4F, 0x67, 0x67, 0x53])} max_read_size = max(len(m) for m in magic_numbers.values()) with open(snd, 'rb') as fd: file_head = fd.read() #print(file_head) if file_head.startswith(magic_numbers['ID3v2']): print ("Plik MP3- z ID3v2") return "mp3" elif file_head.startswith(magic_numbers['ID3v1']): print ("Plik MP3- z ID3v1") return "mp3" elif file_head.startswith(magic_numbers['ID3']): print ("Plik MP3- z ID3") return "mp3" elif file_head.startswith(magic_numbers['withoutID']): print ("Plik MP3 bez ID3") return "mp3" elif file_head.startswith(magic_numbers['ogg']): print("Kontener OGG") ogg_head=mutagen.File(snd).pprint()[0:20] if 'opus' in ogg_head.lower(): return "opus" elif 'flac' in ogg_head.lower(): return "oga" elif 'vorbis'in ogg_head.lower(): return "ogg" def remove_cover_mp3_generic(self,snd): file = ID3(snd) # Load the file file.delall("APIC") # Delete every APIC tag (Cover art) file.save() # Save the file def remove_cover_ogg_generic(self,snd): file = OggVorbis(snd) try: artist=file["artist"] except KeyError: artist="" try: title=file["title"] except KeyError: title="" try: year=file["year_of_relase"] except KeyError: year="" try: album=file["album"] except KeyError: album="" try: genre=file["genre"] except KeyError: genre="" file.delete() file["year_of_relase"]=year file["artist"]=artist file["title"]=title file["album"]=album file["genre"]=genre file.save() def remove_cover_oga_generic(self,snd): file = OggFLAC(snd) try: artist=file["artist"] except KeyError: artist="" try: title=file["title"] except KeyError: title="" try: year=file["year_of_relase"] except KeyError: year="" try: album=file["album"] except KeyError: album="" try: genre=file["genre"] except KeyError: genre="" file.delete() file["year_of_relase"]=year file["artist"]=artist file["title"]=title file["album"]=album file["genre"]=genre file.save() def remove_cover_opus_generic(self,snd): file = OggOpus(snd) #year=file["YEAR_OF_RELEASE"] try: artist=file["artist"] except KeyError: artist="" try: title=file["title"] except KeyError: title="" try: year=file["year_of_relase"] except KeyError: year="" try: album=file["album"] except KeyError: album="" try: genre=file["genre"] except KeyError: genre="" file.delete() file["year_of_relase"]=year file["artist"]=artist file["title"]=title file["album"]=album file["genre"]=genre file.save() class DialogAddCoverProgress(Gtk.Dialog): def __init__(self,parent): Gtk.Dialog.__init__(self, title="Postęp", transient_for=parent, flags=0) self.current_snd=0 self.props.border_width = 20 #self.add_buttons( # Gtk.STOCK_OK, Gtk.ResponseType.OK #) self.progressbar = Gtk.ProgressBar() self.set_default_size(150, 100) box = self.get_content_area() label = Gtk.Label(label="Dodaję okladkę do plików\n") box.add(label) box.add(self.progressbar) self.divider_next=1/len(file_snd_selected) self.timeout_id = GLib.timeout_add(300, self.on_timeout, len(file_snd_selected)) self.activity_mode = False self.show_all() def on_timeout(self, user_data): global img_extension #jesli przepytano wszyskie pliki wówczas koniec if self.current_snd == user_data: self.add_buttons( Gtk.STOCK_OK, Gtk.ResponseType.OK ) self.show_all() return False if self.activity_mode: self.progressbar.pulse() else: new_value = self.progressbar.get_fraction() + self.divider_next #if new_value > 1: # new_value = 0 self.progressbar.set_fraction(new_value) snd=file_snd_selected[self.current_snd] self.current_snd = self.current_snd +1 print ("file selected: ",snd) extension = os.path.splitext(snd) snd_extension=extension[1].replace(".", "") print ("Rozszerzenie pliku dzwiekowego: ", snd_extension) #print ("file selected: ",snd) #extension = os.path.splitext(snd) #snd_extension=extension[1].replace(".", "") #print ("Rozszerzenie pliku dzwiekowego: ", snd_extension) if snd_extension.lower() == "ogg": file_ = OggVorbis(snd) print("IMAGE FILE: ",file_img_selected) with open(file_img_selected, "rb") as h: data = h.read() img_extension=img_extension.lower() picture = Picture() picture.data = data picture.type = 17 picture.desc = u"Picture set by cover2music" picture.mime = u"image/"+img_extension picture.width = 100 picture.height = 100 picture.depth = 24 picture_data = picture.write() encoded_data = base64.b64encode(picture_data) vcomment_value = encoded_data.decode("ascii") file_["metadata_block_picture"] = [vcomment_value] file_.save() elif snd_extension.lower() == "oga": #for snd in file_snd_selected: file_ = OggFLAC(snd) print("IMAGE FILE: ",file_img_selected) with open(file_img_selected, "rb") as h: data = h.read() img_extension=img_extension.lower() picture = Picture() picture.data = data picture.type = 17 # 0x7f picture.desc = u"Picture set by cover2music" picture.mime = u"image/"+img_extension picture.width = 100 picture.height = 100 picture.depth = 24 picture_data = picture.write() encoded_data = base64.b64encode(picture_data) vcomment_value = encoded_data.decode("ascii") file_["metadata_block_picture"] = [vcomment_value] file_.save() elif snd_extension.lower() == "opus": #for snd in file_snd_selected: file_ = OggOpus(snd) print("IMAGE FILE: ",file_img_selected) with open(file_img_selected, "rb") as h: data = h.read() img_extension=img_extension.lower() picture = Picture() picture.data = data picture.type = 17 # 0x7f picture.desc = u"Picture set by cover2music" picture.mime = u"image/"+img_extension #picture.width = 100 #picture.height = 100 #picture.depth = 24 picture_data = picture.write() encoded_data = base64.b64encode(picture_data) vcomment_value = encoded_data.decode("ascii") file_["metadata_block_picture"] = [vcomment_value] file_.save() elif snd_extension.lower() == "mp3": #for snd in file_snd_selected: print ("Dodaje do mp3: ",snd) print ("file_img_selected: ", file_img_selected) file = MP3(snd, ID3=ID3) try: file.add_tags() except error: pass file.tags.add(APIC(mime="image/"+img_extension,type=3,desc=u'Cove by cover2music',data=open(file_img_selected,'rb').read())) file.save() # save the current changes else: print ("źle") # As this is a timeout function, return True so that it # continues to get called return True class DialogHelp(Gtk.Dialog): def __init__(self, parent): Gtk.Dialog.__init__(self, title="Pomoc", transient_for=parent, flags=0) self.props.border_width = 20 self.add_buttons( Gtk.STOCK_OK, Gtk.ResponseType.OK ) self.set_default_size(150, 100) txt="-jednak samo załadownie obrazka do pliku nie gwarantuje, że zmieni się ikona w menedżerze plików.\n W tym względzie są przyjazne menedżery takie jak PCManFM, jak i nieprzyjazne jak Nemo.\n Jednak w graficznym odtwarzaczu, załadowany obrazek powinien pojawić się zawsze\n\n" label1 = Gtk.Label(label="Aplikacja jest w stanie dodać okładkę do prawie wszystkich plików MP3, oraz do plików OGG (Vorbis),\n OGA (OGG FLAC) oraz OPUS. Możliwe jest również usuwanie okładek z pozostawieniem pozostałych tagów.\n Pliki można zmieniać pojedynczo jak i grupowo. Okładki odczytywane są w formatach JPEG oraz PNG.\n Należy zadbać wcześniej aby obrazy okładek nie były zbyt duże, optymalna wielkość to 384x384 px.\n\n") label2 = Gtk.Label(label=" Uwagi:\n-format JPEG w plikach OPUS nie jest wspierany \n-mieszana kolekcja plików zakodowanych rożnymi kodekami w kontenerach OGG jest co prawda możliwa,\n jednak skrypt może zachowywać się niestabilnie\n"+txt) box = self.get_content_area() box.add(label1) box.add(label2) self.show_all() class DialogAbout(Gtk.Dialog): def __init__(self, parent): Gtk.Dialog.__init__(self, title="O Programie", transient_for=parent, flags=0) self.props.border_width = 20 self.add_buttons( Gtk.STOCK_OK, Gtk.ResponseType.OK ) self.set_default_size(150, 100) label = Gtk.Label(label="\tAplikacja napisana na podstawie:\n\nhttps://python-gtk-3-tutorial.readthedocs.io/\n\n\t\t\tWersja: 0.1-3\n\n\t\tPrzemysław R. Pietraszczyk\n\n\t\t\tKwiecień/Maj 2021\n\t\t\tLicencja: GPL\n\n") #label3 = Gtk.Label(label="-jednak samo załadownie obrazka do pliku nie gwarantuje, że zmieni się ikona w menedżerze plików.\n W tym względzie są przyjazne menedżery takie jak PCManFM, jak i nie przyjazne jak Nemo.\n Jednak w graficznym odtwarzaczu, załadowany obrazek powinien pojawić się zawsze.\n\n") box = self.get_content_area() box.add(label) button = Gtk.LinkButton("http://www.prp.xlx.pl", label="Strona Domowa") box.add(button) self.show_all() class DialogNoSelectIMG(Gtk.Dialog): def __init__(self, parent): Gtk.Dialog.__init__(self, title="Błąd", transient_for=parent, flags=0) self.props.border_width = 20 self.add_buttons( Gtk.STOCK_OK, Gtk.ResponseType.OK ) self.set_default_size(150, 100) label = Gtk.Label(label="Nie wybrano żadnego pliku z muzyką!\n") box = self.get_content_area() box.add(label) self.show_all() def get_resource_path(rel_path): dir_of_py_file = os.path.dirname(__file__) rel_path_to_resource = os.path.join(dir_of_py_file, rel_path) abs_path_to_resource = os.path.abspath(rel_path_to_resource) return abs_path_to_resource grid = Gtk.Grid() class MyWindow(Gtk.Window): def __init__(self): super().__init__() self.init_ui() def init_ui(self): global grid, file_img_selected, snd_extension self.image_new=None #grid = Gtk.Grid() self.props.border_width = 20 #self.img_extension="png" # dla przykladu #snd_extension="ogg" self.add(grid) grid.set_row_spacing(10) grid.set_column_spacing(10) grid.set_column_homogeneous(True) # rozszerza kontrolke z utworami na resztę okna action_group = Gtk.ActionGroup(name="my_actions") self.add_file_menu_actions(action_group) self.add_info_menu_actions(action_group) self.add_choices_menu_actions(action_group) uimanager = self.create_ui_manager() uimanager.insert_action_group(action_group) menubar = uimanager.get_widget("/MenuBar") box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) box.pack_start(menubar, False, False, 0) grid.attach(box, 0, 0, 2, 1) file_img_selected=get_resource_path(COVER) pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( filename=file_img_selected, width=100, height=100, preserve_aspect_ratio=True) self.image = Gtk.Image.new_from_pixbuf(pixbuf) grid.attach(self.image,0,1,1,1) quitBtn = Gtk.Button(label="Wykonaj !") quitBtn.set_size_request(60, 30) quitBtn.connect("clicked", self.on_button_clicked) grid.attach(quitBtn, 0, 2, 1, 1) # lewo, top wielkosc, wysokosc self.liststore = Gtk.ListStore(str, str) self.treeview = Gtk.TreeView(model=self.liststore) self.renderer_text = Gtk.CellRendererText() self.column_text = Gtk.TreeViewColumn("Wybrane Utwory", self.renderer_text, text=0) self.treeview.append_column(self.column_text) self.scrolled_window = Gtk.ScrolledWindow () self.scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.scrolled_window.add (self.treeview) grid.attach_next_to(self.scrolled_window, self.image ,Gtk.PositionType.RIGHT,6,2) self.set_border_width(10) self.set_title("Cover2Music") self.set_default_size(650, 180) self.connect("destroy", Gtk.main_quit) self.set_icon_from_file(get_resource_path(COVER)) def on_button_clicked(self, widget): global file_img_selected, snd_extension if len(file_snd_selected)<1: print("nie zaznaczyleś żadnego pliku z muzyką !") dialog = DialogNoSelectIMG(self) response = dialog.run() dialog.destroy() return progress =DialogAddCoverProgress(self) response = progress.run() progress.destroy() def add_info_menu_actions(self, action_group): action_info_menu = Gtk.Action(name="InfoMenu", label="Info") action_group.add_action(action_info_menu) action_new = Gtk.Action( name="Help", label="Pomoc", tooltip="Create a new file", ) action_new.connect("activate", self.on_menu_help) action_group.add_action_with_accel(action_new, None) action_new = Gtk.Action( name="About", label="O Programie", tooltip="Create a new file", ) action_new.connect("activate", self.on_menu_about) action_group.add_action_with_accel(action_new, None) def add_file_menu_actions(self, action_group): action_filemenu = Gtk.Action(name="FileMenu", label="Plik") action_group.add_action(action_filemenu) action_fileloadmenu = Gtk.Action(name="FileLoad", stock_id=Gtk.STOCK_NEW) action_group.add_action(action_fileloadmenu) action_new = Gtk.Action( name="LoadImg", label="Załaduj okladkę", tooltip="Wybór okladki", ) action_new.connect("activate", self.on_menu_file_load_img_generic) action_group.add_action_with_accel(action_new, None) action_new = Gtk.Action( name="LoadOgg", label="Załaduj muzykę", tooltip="Wybór muzyki", ) action_new.connect("activate", self.on_menu_file_load_ogg_generic) action_group.add_action_with_accel(action_new, None) action_fileloadmenu = Gtk.Action(name="FileDelete", label="Usuń", stock_id=None) action_group.add_action(action_fileloadmenu) action_new = Gtk.Action( name="RemoveCover", label="Usuwa okladkę", tooltip="Usuwa okladkę z pliku", ) action_new.connect("activate", self.on_menu_file_erase_cover_all) action_group.add_action_with_accel(action_new, None) action_filequit = Gtk.Action(name="FileQuit", stock_id=Gtk.STOCK_QUIT) action_filequit.connect("activate", self.on_menu_file_quit) action_group.add_action(action_filequit) def add_choices_menu_actions(self, action_group): action_group.add_action(Gtk.Action(name="ChoicesMenu", label="Opcje")) three = Gtk.ToggleAction(name="ChoiceTree", label="Pozwolenie") three.connect("toggled", self.on_menu_choices_toggled) action_group.add_action(three) # tworzy menu bar def create_ui_manager(self): uimanager = Gtk.UIManager() # Throws exception if something went wrong uimanager.add_ui_from_string(UI_INFO) # Add the accelerator group to the toplevel window accelgroup = uimanager.get_accel_group() self.add_accel_group(accelgroup) return uimanager def on_menu_help(self, widget): dialog = DialogHelp(self) response = dialog.run() dialog.destroy() def on_menu_about(self, widget): dialog = DialogAbout(self) response = dialog.run() dialog.destroy() def on_menu_file_load_img_generic(self, widget): global grid, file_img_selected, img_extension print("A File|New menu item was selected.") file=FileChooserIMG() print ("file:"+str(file_img_selected)) pixbuf_new = GdkPixbuf.Pixbuf.new_from_file_at_scale( filename=file_img_selected, width=100, height=100, preserve_aspect_ratio=True) if self.image != None: grid.remove(self.image) self.image=None else: grid.remove(self.image_new) # TODO - pozbyć się self.image_new na rzecz self.image self.image_new = Gtk.Image.new_from_pixbuf(pixbuf_new) grid.attach_next_to(self.image_new, self.scrolled_window,Gtk.PositionType.LEFT,1,1) self.show_all() extension = os.path.splitext(file_img_selected) #self.img_extension=extension[1].replace(".", "") # tupla (0) wskazuje na nazwe pliku ! img_extension=extension[1].replace(".", "") # tupla (0) wskazuje na nazwe pliku ! def on_menu_file_load_ogg_generic(self, widget): global file_snd_selected file=FileChooserOgg() #grid.remove(self.treeview) self.scrolled_window.remove(self.treeview) grid.remove(self.scrolled_window) self.liststore = Gtk.ListStore(str, str) self.treeview.remove_column(self.column_text) print ("file selected: ",file_snd_selected) for o in file_snd_selected: self.liststore.append([o,""]) self.scrolled_window = Gtk.ScrolledWindow () self.scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) self.treeview = Gtk.TreeView(model=self.liststore) self.renderer_text = Gtk.CellRendererText() self.column_text = Gtk.TreeViewColumn("Wybrane Utwory", self.renderer_text, text=0) self.treeview.append_column(self.column_text) if self.image != None: self.scrolled_window.add (self.treeview) grid.attach_next_to(self.scrolled_window, self.image ,Gtk.PositionType.RIGHT,6,2) else: self.scrolled_window.add (self.treeview) grid.attach_next_to(self.scrolled_window, self.image_new ,Gtk.PositionType.RIGHT,6,2) self.show_all() """ def close_progress(self, gparmstring): self.progress.destroy() """ def on_menu_file_erase_cover_all(self, widget): if len(file_snd_selected)<1: dialog = DialogNoSelectIMG(self) response = dialog.run() dialog.destroy() return self.progress =DialogDeleteProgress(self) response = self.progress.run() self.progress.destroy() def on_menu_file_quit(self, widget): Gtk.main_quit() def on_menu_choices_toggled(self, widget): if widget.get_active(): print(widget.get_name() + " activated") else: print(widget.get_name() + " deactivated") def on_button_press_event(self, widget, event): # Check if right mouse button was preseed if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3: self.popup.popup(None, None, None, None, event.button, event.time) return True # event has been handled win = MyWindow() win.show_all() Gtk.main()