Advertisement
Briga

fixrhygtk

May 2nd, 2018
142
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 32.92 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # Copyright 2016 Paolo Pelloni
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining
  5. # a copy of this software and associated documentation files (the
  6. # "Software"), to deal in the Software without restriction, including
  7. # without limitation the rights to use, copy, modify, merge, publish,
  8. # distribute, sublicense, and/or sell copies of the Software, and to
  9. # permit persons to whom the Software is furnished to do so, subject
  10. # to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included
  13. # in all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  18. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  19. # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  20. # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  21. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22.  
  23. ########################################################################
  24. # CONSTANTS
  25. ########################################################################
  26.  
  27. VERSION = "1.0"
  28.  
  29. ########################################################################
  30. # App main class
  31. ########################################################################
  32.  
  33. class FixRhyGtk:
  34.    
  35.     def __init__(self, argv):
  36.         try:
  37.             opts, args = getopt.getopt(argv, 'd', '' )
  38.         except:
  39.             print "fixrhygtk [-d]"
  40.             sys.exit(1)
  41.         # checking for debug
  42.         self.debug = False
  43.         for opt in opts:   
  44.             if opt == '-d':
  45.                 self.debug = True
  46.         self.modified = False
  47.         self.loaded = False
  48.         self._lock = threading.Lock()
  49.         self.init_settings()
  50.         self.load_settings()
  51.         self.backup_filename = self.DB_filename + ".backup"
  52.         glade_filename = 'fixrhygtk.glade'
  53.         if os.path.isfile(glade_filename) == False:
  54.             glade_filename = '/usr/share/fixrhygtk/' + glade_filename
  55.             if self.debug:
  56.                 print "No local glade file, looking for", glade_filename
  57.             if os.path.isfile(glade_filename) == False:
  58.                 print "No glade file found, exiting!"
  59.                 sys.exit(1)
  60.         self.load_from_glade(glade_filename)
  61.         self.tbtn_filter.set_sensitive(False)
  62.         self.tbtn_filter.set_active(self.only_missing)
  63.         self.tbtn_find_next.set_sensitive(False)
  64.         self.setup_treeview()
  65.         self.main_window.show()
  66.         self.check_rhythmbox(False)
  67.  
  68.     ####################################################################
  69.     # Load resources from glade file
  70.     ####################################################################
  71.    
  72.     def load_from_glade(self, glade_filename):
  73.         self.builder = Gtk.Builder()
  74.         self.builder.add_from_file(glade_filename)
  75.         self.builder.connect_signals(self)
  76.         self.builder.connect_signals({"onDeleteWindow": Gtk.main_quit})
  77.         self.main_window = self.builder.get_object("MainWindow")
  78.         self.main_box = self.builder.get_object("MainBox")
  79.         self.main_scroll_window = self.builder.get_object("MainScrollWindow")
  80.         self.main_treeview_selection = self.builder.get_object("TreeViewSelection")
  81.         self.status_bar = self.builder.get_object("MyStatusBar")
  82.         self.status_bar_context_id = self.status_bar.get_context_id("info")
  83.         self.tbtn_filter = self.builder.get_object("tbtn_filter")
  84.         self.tbtn_find_next = self.builder.get_object("tbtn_find_next")
  85.         self.progress_window = self.builder.get_object("ProgressWindow")
  86.         self.progress_bar = self.builder.get_object("ProgressBar")
  87.         self.progress_label = self.builder.get_object("ProgressLabel")
  88.         self.main_treeview = self.builder.get_object("MainTreeView")
  89.         self.about_dialog = self.builder.get_object("AboutDialog")
  90.          
  91.     ####################################################################
  92.     # Configuration
  93.     ####################################################################
  94.    
  95.     ####################################################################
  96.     # Initialise and eventually create settings
  97.     ####################################################################
  98.        
  99.     def init_settings(self):
  100.         self.settings_path = "org.gnome.fixrhygtk"
  101.         self.settings = Gio.Settings(self.settings_path)
  102.        
  103.     ####################################################################
  104.     # Loads settings
  105.     ####################################################################
  106.    
  107.     def load_settings(self):
  108.         self.DB_filename = self.settings.get_string("db-filename")
  109.         self.only_missing = self.settings.get_boolean("show-only-missing")
  110.         self.force = self.settings.get_boolean("force")
  111.        
  112.     ####################################################################
  113.     # Save settings
  114.     ####################################################################
  115.        
  116.     def save_settings(self):
  117.         self.settings.set_string("db-filename", self.DB_filename)
  118.         self.settings.set_boolean("show-only-missing", self.only_missing)
  119.         self.settings.set_boolean("force", self.force)
  120.         self.settings.sync()
  121.    
  122.     ####################################################################
  123.     # Prepares the treeview
  124.     ####################################################################
  125.    
  126.     def setup_treeview(self):
  127.         self.add_column("Artist", 0, False)
  128.         self.add_column("Album", 1, False)
  129.         self.add_column("Year", 2, False)
  130.         self.add_column("Sort Artist", 3, True)
  131.         self.add_column("Sort Album", 4, True)     
  132.         self.add_column("Track", 5, False)
  133.         self.add_column("Title", 6, False)
  134.         self.liststore = Gtk.ListStore(str, str, str, str, str, str, str)
  135.         self.main_treeview.set_model(self.liststore)
  136.        
  137.     ####################################################################
  138.     # Updates the treeview
  139.     ####################################################################
  140.    
  141.     def update_list_view(self):
  142.        self.cell0 = Gtk.CellRendererText()
  143.  
  144.     ####################################################################
  145.     # Add a column to the treeview
  146.     ####################################################################
  147.    
  148.     def add_column(self, title, column_id, is_editable ):  
  149.         rendered_text = Gtk.CellRendererText()
  150.         rendered_text.connect('edited', self.field_edited_handler, column_id)
  151.         rendered_text.set_property('editable', is_editable)
  152.         column = Gtk.TreeViewColumn(title, rendered_text, text=column_id)
  153.         column.set_sort_column_id(column_id)
  154.         column.set_resizable(True)
  155.         self.main_treeview.append_column(column)
  156.                
  157.     ####################################################################
  158.     # Event handlers
  159.     ####################################################################
  160.    
  161.     ####################################################################
  162.     # Main Window event handlers
  163.     ####################################################################
  164.    
  165.     def on_main_window_destroy(self, *args):
  166.         if self.modified:
  167.             confirm_text = "Data has not been saved. If you proceed all the changes will be lost. Do you want to quit?"
  168.             confirm_dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, confirm_text)
  169.             confirm_dialog.set_transient_for(self.main_window)
  170.             response = confirm_dialog.run()
  171.             confirm_dialog.destroy()      
  172.             if Gtk.ResponseType(response) is not Gtk.ResponseType.YES:
  173.                 return
  174.         Gtk.main_quit(*args)           
  175.  
  176.     ####################################################################
  177.     # toolbars button event handlers
  178.     ####################################################################
  179.    
  180.     def on_tbtn_load_clicked(self, widget):
  181.         if self.modified:
  182.             confirm_text = "Data has not been saved. If you proceed all the changes will be lost. Do you want to reload data?"
  183.             confirm_dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.YES_NO, confirm_text)
  184.             response = confirm_dialog.run()
  185.             confirm_dialog.destroy()      
  186.             if Gtk.ResponseType(response) is not Gtk.ResponseType.YES:
  187.                 return
  188.         self.liststore.clear()
  189.         self.thread = threading.Thread(target=self.load_DB)
  190.         self.thread.start()
  191.         self.update_list_view()
  192.        
  193.     def on_tbtn_save_clicked(self, widget):
  194.         self.save_DB()
  195.        
  196.     def on_tbtn_find_clicked(self, widget):
  197.         search_dialog = self.builder.get_object("SearchDialog")
  198.         search_dialog_text = self.builder.get_object("search_entry")
  199.         search_dialog_text.set_text("")
  200.         search_dialog.set_focus(search_dialog_text)
  201.         response = search_dialog.run()
  202.         search_dialog.hide()
  203.         if Gtk.ResponseType(response) is not Gtk.ResponseType.CANCEL:
  204.             self.searching = search_dialog_text.get_text()
  205.             self.tbtn_find_next.set_sensitive(True)
  206.             self.last_searched = self.search_treeview(self.searching, None)
  207.                    
  208.     def on_tbtn_find_next_clicked(self, widget):
  209.         self.last_searched = self.search_treeview(self.searching, self.last_searched)
  210.        
  211.     def on_tbtn_filter_toggled(self, widget):
  212.         if self.debug:
  213.             print "toggled"
  214.         if not self.loaded:
  215.             return
  216.         self.only_missing = not self.only_missing
  217.         self.liststore.clear()
  218.         thread = threading.Thread(target=self.show_data,args=(self.only_missing,))
  219.         thread.start()
  220.        
  221.     def on_tbtn_info_clicked(self, widget):
  222.         self.show_tracks_info()
  223.            
  224.     def on_tbtn_fill_clicked(self, widget):
  225.         self.try_filling()
  226.  
  227.     def on_tbtn_fill_ask_clicked(self, widget):
  228.         self.try_filling_ask()
  229.        
  230.     def on_tbtn_fill_text_clicked(self, widget):
  231.         self.try_filling_text()
  232.        
  233.     def on_tbtn_preferences_clicked(self, widget):
  234.         pref_window = self.builder.get_object("SettingsDialog")
  235.         pref_fcb = self.builder.get_object("fcb_db")
  236.         pref_checkbox = self.builder.get_object("chk_show_missing")
  237.         pref_checkboxf = self.builder.get_object("chk_force")
  238.         db_file = Gio.File.new_for_path(self.DB_filename)
  239.         pref_fcb.set_file(db_file)
  240.         pref_checkbox.set_active(self.only_missing)
  241.         pref_window.set_modal(True)
  242.         resp = pref_window.run()
  243.         if Gtk.ResponseType(resp) is Gtk.ResponseType.OK:
  244.             db_file = pref_fcb.get_file()
  245.             self.DB_filename = db_file.get_path()
  246.             self.only_missing = pref_checkbox.get_active()
  247.             self.force = pref_checkboxf.get_active()
  248.             self.save_settings()
  249.         pref_window.hide()
  250.        
  251.     def on_tbtn_about_clicked(self, widget):
  252.         self.about_dialog.set_version(VERSION)
  253.         self.about_dialog.run()
  254.         self.about_dialog.hide()
  255.        
  256.     def on_btn_previous_clicked(self, widget):
  257.         if self.info_pos > 0:
  258.             self.info_pos = self.info_pos - 1
  259.             self.show_tracks_info_data()
  260.            
  261.     def on_btn_next_clicked(self, widget):
  262.         if self.info_pos < (len(self.i_pathlist)-1):
  263.             self.info_pos = self.info_pos + 1
  264.             self.show_tracks_info_data()
  265.    
  266.     ####################################################################
  267.     # Responds to treeview right clic
  268.     ####################################################################
  269.  
  270.     def on_tvButtonPress(self, widget, event):
  271.         if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
  272.             x = int(event.x)
  273.             y = int(event.y)
  274.             pthinfo = self.main_treeview.get_path_at_pos(x, y)
  275.             if pthinfo is not None:
  276.                 path, col, cellx, celly = pthinfo
  277.                 self.main_treeview.grab_focus()
  278.                 self.main_treeview.set_cursor(path, col, 0)
  279.                 (model, pathlist) = self.main_treeview_selection.get_selected_rows()
  280.                 tree_iter = model.get_iter(pathlist[0])
  281.                 if tree_iter is not None:
  282.                     selected_artist = model.get_value(tree_iter, 0)
  283.                     selected_album = model.get_value(tree_iter, 1)
  284.                     selected_year = model.get_value(tree_iter, 2)
  285.                     selected_sort_artist = model.get_value(tree_iter, 3)
  286.                     selected_sort_album = model.get_value(tree_iter, 4)
  287.                     selected_track = model.get_value(tree_iter, 5)
  288.                     selected_title = model.get_value(tree_iter, 6)
  289.                     self.popup_menu = Gtk.Menu()
  290.                     option1 = Gtk.MenuItem(selected_artist)
  291.                     option1.connect("activate", self.menu_clicked_handler, selected_artist, selected_year)
  292.                     self.popup_menu.append(option1)
  293.                     t = selected_artist
  294.                     if ' ' in selected_artist:
  295.                         if selected_artist.startswith('The'):
  296.                            t = selected_artist[4:] + ", The"
  297.                         else:
  298.                            first_space = selected_artist.find(' ')
  299.                            t = selected_artist[first_space+1:] + ", " + selected_artist[:first_space]
  300.                     option2 = Gtk.MenuItem(t)
  301.                     option2.connect('activate', self.menu_clicked_handler, t, selected_year)
  302.                     self.popup_menu.append(option2)
  303.                 self.popup_menu.show_all()
  304.                 self.popup_menu.popup(None, None, None, None, event.button, event.time)
  305.    
  306.     ####################################################################               
  307.     # Cellrenderer edited  
  308.     ####################################################################
  309.    
  310.     def field_edited_handler(self, widget, path, text, column):
  311.         if self.debug:
  312.            print "edited", widget, path, column, text
  313.         self.liststore[path][column] = text
  314.         self.update_list_view()
  315.         return True
  316.  
  317.     ####################################################################
  318.     # Popup menu handlers
  319.     ####################################################################
  320.    
  321.     def menu_clicked_handler(self, widget, artist, album):
  322.         if self.debug:
  323.             print "clicked", artist, album
  324.         (model, pathlist) = self.main_treeview_selection.get_selected_rows()
  325.         for path in pathlist:
  326.             tree_iter = model.get_iter(path)
  327.             selected_artist = model.get_value(tree_iter, 0)
  328.             selected_album = model.get_value(tree_iter, 1)
  329.             selected_year = model.get_value(tree_iter, 2)
  330.             selected_track = model.get_value(tree_iter, 5)
  331.             selected_title = model.get_value(tree_iter, 6)
  332.             model.set_value(tree_iter, 3, artist)
  333.             model.set_value(tree_iter, 4, album)
  334.             self.update_record(selected_artist, selected_album, selected_track, selected_title,
  335.                                artist, album ,self.force)  
  336.        
  337.     ####################################################################
  338.     # Other methods
  339.     ####################################################################
  340.  
  341.     ####################################################################
  342.     # Load DB from file into XML managing structure
  343.     ####################################################################
  344.    
  345.     def load_DB(self):
  346.         if self.debug:
  347.             print "Checking file"
  348.         if os.path.isfile(self.DB_filename) == False:
  349.             print "No file", self.DB_filename, "found!"
  350.             return
  351.         if os.access(self.DB_filename, os.W_OK) == False:
  352.             print "No access to file", self.DB_filename, "!"
  353.             return
  354.         self.status_bar.push(self.status_bar_context_id, 'Loading database...' + self.DB_filename)
  355.         self.tree = ET.ElementTree(file=self.DB_filename)
  356.         self.status_bar.push(self.status_bar_context_id, 'Database loaded')
  357.         self.modified = False
  358.         self.loaded = True
  359.         with self._lock:
  360.             self.show_data(self.only_missing)
  361.         self.tbtn_filter.set_sensitive(True)
  362.        
  363.     ####################################################################
  364.     # Saves the structure to the xml file
  365.     ####################################################################
  366.    
  367.     def save_DB(self):
  368.         if not self.loaded:
  369.             return
  370.         self.progress_bar.set_fraction(0)
  371.         self.progress_label.set_text("Saving database")
  372.         self.progress_window.show()
  373.         while Gtk.events_pending():
  374.             Gtk.main_iteration()
  375.        
  376.         # backup file
  377.         self.status_bar.push(self.status_bar_context_id, 'Creating a backup...')
  378.         shutil.copy2(self.DB_filename, self.backup_filename)
  379.        
  380.         self.progress_bar.set_fraction(0.5)
  381.         self.progress_bar.pulse()
  382.         while Gtk.events_pending():
  383.             Gtk.main_iteration()
  384.  
  385.         # write modified one
  386.         self.tree.write(self.DB_filename, xml_declaration=True)
  387.         self.status_bar.push(self.status_bar_context_id, 'Database saved: ' + self.DB_filename)
  388.         self.modified = False
  389.        
  390.         self.progress_window.hide()
  391.        
  392.     ####################################################################
  393.     # Informations on track
  394.     ####################################################################
  395.    
  396.     def show_tracks_info(self):
  397.         (self.i_model, self.i_pathlist) = self.main_treeview_selection.get_selected_rows()
  398.         if self.debug:
  399.             print len(self.i_pathlist)
  400.         if len(self.i_pathlist) == 0:
  401.             return
  402.         self.info_dialog = self.builder.get_object("InfoDialog")
  403.         if len(self.i_pathlist) == 1:
  404.             buttons = False
  405.         else:
  406.             buttons = True
  407.         btn_prev = self.builder.get_object("btn_previous")
  408.         btn_prev.set_sensitive(buttons)
  409.         btn_next = self.builder.get_object("btn_next")
  410.         btn_next.set_sensitive(buttons)
  411.         self.info_pos = 0
  412.         self.show_tracks_info_data()
  413.         self.info_dialog.run()
  414.         self.info_dialog.hide()
  415.        
  416.     ####################################################################
  417.     # Show track information (only for self.info_pos)
  418.     ####################################################################
  419.    
  420.     def show_tracks_info_data(self):
  421.         tree_iter = self.i_model.get_iter(self.i_pathlist[self.info_pos])
  422.         txt_title = self.builder.get_object("txt_title")
  423.         title = self.i_model.get_value(tree_iter, 6)
  424.         txt_title.set_text(title)
  425.         txt_artist = self.builder.get_object("txt_artist")
  426.         artist = self.i_model.get_value(tree_iter, 0)
  427.         txt_artist.set_text(artist)
  428.         txt_album = self.builder.get_object("txt_album")
  429.         album = self.i_model.get_value(tree_iter, 1)
  430.         txt_album.set_text(album)
  431.         txt_track = self.builder.get_object("txt_track")
  432.         track = self.i_model.get_value(tree_iter, 5)
  433.         txt_track.set_text(track)
  434.         txt_artist_sort = self.builder.get_object("txt_artist_sort")
  435.         artist_sort = self.i_model.get_value(tree_iter, 3)
  436.         if artist_sort is not None:
  437.             txt_artist_sort.set_text(artist_sort)
  438.         else:
  439.             txt_artist_sort.set_text('')           
  440.         txt_album_sort = self.builder.get_object("txt_album_sort")
  441.         album_sort = self.i_model.get_value(tree_iter, 4)
  442.         if album_sort is not None:
  443.             txt_album_sort.set_text(album_sort)
  444.         else:
  445.             txt_album_sort.set_text('')        
  446.         txt_date = self.builder.get_object("txt_date")
  447.         txt_date.set_text(self.i_model.get_value(tree_iter, 2))
  448.         song = self.track_info(artist, album, track, title)
  449.         if song is None:
  450.             return
  451.         txt_disk = self.builder.get_object("txt_disk")
  452.         txt_disk.set_text(song.findtext('disc-number', ' '))
  453.         txt_composer = self.builder.get_object("txt_composer")
  454.         txt_composer.set_text(song.findtext('composer', ' '))
  455.         txt_genre = self.builder.get_object("txt_genre")
  456.         txt_genre.set_text(song.findtext('genre', ' '))
  457.         txt_duration = self.builder.get_object("txt_duration")
  458.         txt_duration.set_text(self.duration_convert(song.findtext('duration', ' ')))
  459.         txt_filesize = self.builder.get_object("txt_filesize")
  460.         txt_filesize.set_text(song.findtext('file-size', ' '))
  461.         txt_playcount = self.builder.get_object("txt_playcount")
  462.         txt_playcount.set_text(song.findtext('play-count', ' '))
  463.         txt_comment = self.builder.get_object("txt_comment")
  464.         txt_comment.set_text(song.findtext('comment', ' '))
  465.        
  466.  
  467.     ####################################################################
  468.     # Shows loaded data in the treeview (all or only one missing sort informations)
  469.     ####################################################################
  470.    
  471.     def show_data(self, show_only_missing):
  472.         if self.debug:
  473.             print "Showing > " + str(show_only_missing)
  474.         if not self.loaded:
  475.             return
  476.         root = self.tree.getroot()
  477.         count = 0
  478.         songs = 0
  479.         for entry in root:
  480.             if entry.attrib == {'type': 'song'}:
  481.                 songs = songs + 1
  482.                 artist_sort_name = entry.findtext('mb-artistsortname', None)
  483.                 album_sort_name = entry.findtext('album-sortname', None)
  484.                 if show_only_missing == False or artist_sort_name == None or album_sort_name == None:
  485.                     artist_name = entry.find('artist').text
  486.                     album_name = entry.find('album').text
  487.                     date_pubblished = entry.find('date').text
  488.                     year_pubblished = str(self.posix_to_year(date_pubblished))
  489.                     if year_pubblished == None:
  490.                         year_pubblished = "SW"
  491.                     track_title = entry.findtext('title', ' ')
  492.                     track_number = entry.findtext('track-number', ' ') 
  493.                     self.liststore.append([artist_name, album_name, year_pubblished, artist_sort_name,
  494.                                           album_sort_name, track_number, track_title])
  495.                     count = count + 1
  496.         if self.debug:
  497.             print "end"
  498.         msg = str(songs) + " songs loaded, " + str(count) + " listed"
  499.         self.status_bar.push(self.status_bar_context_id, msg)
  500.         #self.update_list_view()
  501.        
  502.     ####################################################################
  503.     # Searches for the first entry in the treeview
  504.     ####################################################################
  505.    
  506.     def search_treeview(self, src, current):
  507.         if current is None:
  508.             current = self.liststore.get_iter_first()
  509.         else:
  510.             current = self.liststore.iter_next(current)
  511.         while current is not None:
  512.             if src in self.liststore.get_value(current, 0):
  513.                 if self.debug:
  514.                     print self.liststore.get_value(current, 0)
  515.                 col = self.main_treeview.get_column(3)
  516.                 self.main_treeview.set_cursor_on_cell(self.liststore.get_path(current), col, self.cell0, True)
  517.                 return current
  518.             current = self.liststore.iter_next(current)
  519.         self.not_found(src)
  520.                
  521.     ####################################################################
  522.     # Shows a not found dialog
  523.     ####################################################################
  524.    
  525.     def not_found(self, txt):
  526.         confirm_text = txt + " not found!"
  527.         confirm_dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, confirm_text)
  528.         confirm_dialog.set_transient_for(self.main_window)
  529.         response = confirm_dialog.run()
  530.         confirm_dialog.destroy()      
  531.  
  532.     ####################################################################
  533.     # Try filling the line with the most obvious proposort_album
  534.     ####################################################################
  535.    
  536.     def try_filling(self):
  537.         if self.debug:
  538.             print "Get selected lines"
  539.         (model, pathlist) = self.main_treeview_selection.get_selected_rows()
  540.         for path in pathlist:
  541.             tree_iter = model.get_iter(path)
  542.             selected_artist = model.get_value(tree_iter, 0)
  543.             selected_album = model.get_value(tree_iter, 1)
  544.             selected_year = model.get_value(tree_iter, 2)
  545.             selected_sort_artist = model.get_value(tree_iter, 3)
  546.             selected_sort_album = model.get_value(tree_iter, 4)
  547.             selected_track = model.get_value(tree_iter, 5)
  548.             selected_title = model.get_value(tree_iter, 6)
  549.             if self.debug:
  550.                 print selected_artist, " ", selected_album, " ", selected_year
  551.             if ' ' in selected_artist:
  552.                 if selected_artist.startswith('The'):
  553.                     proposed_sort_artist = selected_artist[4:] + ", The"
  554.                 else:
  555.                     first_space = selected_artist.find(' ')
  556.                     proposed_sort_artist = selected_artist[first_space+1:] + ", " + selected_artist[:first_space]
  557.             else:
  558.                 proposed_sort_artist = selected_artist
  559.             proposed_sort_album = selected_year
  560.             model.set_value(tree_iter, 3, proposed_sort_artist)
  561.             model.set_value(tree_iter, 4, proposed_sort_album)
  562.             self.update_record(selected_artist, selected_album, selected_track, selected_title,
  563.                                proposed_sort_artist, proposed_sort_album ,self.force)  
  564.  
  565.     ####################################################################
  566.     # try filling the line with the most obvious proposort_album but asking for best choice
  567.     ####################################################################
  568.    
  569.     def try_filling_ask(self):
  570.         if self.debug:
  571.             print "Get selected lines"
  572.         (model, pathlist) = self.main_treeview_selection.get_selected_rows()
  573.         already_found = [] # here I will store the one found and asked
  574.         already_chosen = [] # and here the user selection
  575.         for path in pathlist:
  576.             tree_iter = model.get_iter(path)
  577.             selected_artist = model.get_value(tree_iter, 0)
  578.             selected_album = model.get_value(tree_iter, 1)
  579.             selected_year = model.get_value(tree_iter, 2)
  580.             selected_sort_artist = model.get_value(tree_iter, 3)
  581.             selected_sort_album = model.get_value(tree_iter, 4)
  582.             selected_track = model.get_value(tree_iter, 5)
  583.             selected_title = model.get_value(tree_iter, 6)
  584.             if self.debug:
  585.                 print selected_artist, " ", selected_album, " ", selected_year
  586.             if selected_artist not in already_found:
  587.                 options = []
  588.                 if self.debug:
  589.                     print "options cleared"
  590.                 options_index = 0
  591.                 selection_dialog = Gtk.Dialog(title = "Choose the sort artist")
  592.                 label = Gtk.Label("Choose sort artist ")
  593.                 selection_dialog.vbox.pack_start(label, True, True, 0)
  594.                 label.show()
  595.                 selection_dialog.get_action_area().set_orientation(Gtk.Orientation.VERTICAL)
  596.                 selection_dialog.set_modal(True)
  597.                 selection_dialog.set_transient_for(self.main_window)
  598.                 options.append(selected_artist)
  599.                 selection_dialog.add_button(selected_artist, options_index) # option 1 is artist as it is
  600.                 index = 0
  601.                 while index < len(selected_artist):
  602.                     index = selected_artist.find(' ', index)
  603.                     if index == -1:
  604.                         break
  605.                     t = selected_artist[index+1:] + ", " + selected_artist[:index]
  606.                     options_index = options_index + 1
  607.                     index = index + 1
  608.                     options.append(t)
  609.                     selection_dialog.add_button(t, options_index)
  610.                 index = index + 1
  611.                 selection_dialog.add_button("Stop and exit", -6)
  612.                 selected = selection_dialog.run()
  613.                 selection_dialog.destroy()
  614.                 if selected == -6:
  615.                     return
  616.                 while Gtk.events_pending():
  617.                     Gtk.main_iteration()
  618.                 if self.debug:
  619.                     print selected
  620.                 proposed_sort_artist = options[selected]
  621.                 if self.debug:
  622.                     print proposed_sort_artist
  623.                 already_found.append(selected_artist)
  624.                 already_chosen.append(proposed_sort_artist)
  625.             else:
  626.                 proposed_sort_artist = already_chosen[already_found.index(selected_artist)]
  627.             proposed_sort_album = selected_year
  628.             model.set_value(tree_iter, 3, proposed_sort_artist)
  629.             model.set_value(tree_iter, 4, proposed_sort_album)
  630.             self.update_record(selected_artist, selected_album, selected_track, selected_title,
  631.                                proposed_sort_artist, proposed_sort_album ,self.force)  
  632.  
  633.     ####################################################################
  634.     # try filling the line with text given by user
  635.     ####################################################################
  636.  
  637.     def try_filling_text(self):
  638.         if self.debug:
  639.             print "Get selected lines"
  640.         (model, pathlist) = self.main_treeview_selection.get_selected_rows()
  641.         sort_dialog = self.builder.get_object("SortDialog")
  642.         cbe_artist = self.builder.get_object("box_artist_sort")
  643.         cbe_artist.get_model().clear()
  644.         cbe_album = self.builder.get_object("box_album_sort")
  645.         tree_iter = model.get_iter(pathlist[0])
  646.         selected_artist = model.get_value(tree_iter, 0)
  647.         cbe_artist.get_child().set_text(selected_artist)
  648.         index = 0
  649.         while index < len(selected_artist):
  650.             index = selected_artist.find(' ', index)
  651.             if index == -1:
  652.                 break
  653.             t = selected_artist[index+1:] + ", " + selected_artist[:index]
  654.             index = index + 1
  655.             cbe_artist.append_text(t)
  656.         cbe_album.get_child().set_text(model.get_value(tree_iter,2))
  657.         response = sort_dialog.run()
  658.         sort_dialog.hide()
  659.         if response == Gtk.ResponseType.CANCEL:
  660.             return
  661.         proposed_sort_artist = cbe_artist.get_child().get_text()
  662.         proposed_sort_album = cbe_album.get_child().get_text()
  663.         if self.debug:
  664.             print "Got:", proposed_sort_artist, proposed_sort_album
  665.         for path in pathlist:
  666.             tree_iter = model.get_iter(path)
  667.             selected_artist = model.get_value(tree_iter, 0)
  668.             selected_album = model.get_value(tree_iter, 1)
  669.             selected_year = model.get_value(tree_iter, 2)
  670.             selected_sort_artist = model.get_value(tree_iter, 3)
  671.             selected_sort_album = model.get_value(tree_iter, 4)
  672.             selected_track = model.get_value(tree_iter, 5)
  673.             selected_title = model.get_value(tree_iter, 6)
  674.             if self.debug:
  675.                 print selected_artist, " ", selected_album, " ", selected_year
  676.             model.set_value(tree_iter, 3, proposed_sort_artist)
  677.             model.set_value(tree_iter, 4, proposed_sort_album)
  678.             self.update_record(selected_artist, selected_album, selected_track, selected_title,
  679.                                proposed_sort_artist, proposed_sort_album ,self.force)
  680.                                
  681.     ####################################################################
  682.     # Returns the track information
  683.     ####################################################################
  684.    
  685.     def track_info(self, artist, album, track, title):
  686.         root = self.tree.getroot()
  687.         if self.debug:
  688.             print "Searching for:", artist, album, track, title
  689.         for entry in root:
  690.             if entry.attrib == {'type': 'song'}:
  691.                 if entry.find('artist').text == artist:
  692.                     if entry.find('album').text == album:
  693.                         if entry.find('title').text == title:
  694.                             if entry.find('track-number').text == track:
  695.                                 return entry
  696.         return None
  697.  
  698.     ####################################################################
  699.     # update the record in the xml structure
  700.     ####################################################################
  701.    
  702.     def update_record(self, artist, album, track, title, sort_artist, sort_album, is_forced):
  703.         self.modified = True
  704.         root = self.tree.getroot()
  705.         if self.debug:
  706.             print "Trying update, looking for", artist, album, track, title
  707.         for entry in root:
  708.             process = False
  709.             if entry.attrib == {'type': 'song'}:
  710.                 if entry.find('artist').text == artist:
  711.                     if entry.find('album').text == album:
  712.                         if entry.find('title').text == title:
  713.                             process = True
  714.             if process:
  715.                 artist_name = entry.find('mb-artistsortname')
  716.                 if artist_name == None:
  717.                     se = ET.SubElement(entry, 'mb-artistsortname')
  718.                     se.text = sort_artist
  719.                     if self.debug:
  720.                        print "Set sort artist", sort_artist
  721.                 else:
  722.                     if artist_name.text == sort_artist:
  723.                         if self.debug:
  724.                             print "Sort artist already OK"
  725.                     else:
  726.                         if is_forced:
  727.                             entry.remove(artist_name)
  728.                             se = ET.SubElement(entry, 'mb-artistsortname')
  729.                             se.text = sort_artist
  730.                             if self.debug:
  731.                                 print "Sort artist different, force changed"
  732.                         else:
  733.                             if self.debug:
  734.                                 print "Sort artist different, left unchanged"
  735.                 album_name = entry.find('album-sortname')
  736.                 if album_name == None:
  737.                     se = ET.SubElement(entry, 'album-sortname')
  738.                     se.text = sort_album
  739.                     if self.debug:
  740.                         print "Set sort album",sort_album
  741.                 else:
  742.                     if album_name.text == sort_album:
  743.                         if self.debug:
  744.                             print "Sort album already OK"
  745.                     else:
  746.                         if is_forced:
  747.                             entry.remove(album_name)
  748.                             se = ET.SubElement(entry, 'album-sortname')
  749.                             se.text = sort_album
  750.                             if self.debug:
  751.                                 print "Sort album different, force changed"
  752.                         else:
  753.                             if self.debug:
  754.                                 print "Sort album different, left unchanged"
  755.    
  756.     ####################################################################
  757.     # convert rythmbox date format to 'human' year
  758.     ####################################################################
  759.    
  760.     def posix_to_year(self, psx_str):
  761.         psx = int(psx_str)
  762.         year = 2039
  763.         check = 744365
  764.         leap_count = 1
  765.         while check > 650000:
  766.             if psx == check:
  767.                 return year
  768.             if leap_count < 3:
  769.                 leap_count = leap_count + 1
  770.                 check = check - 365
  771.                 year = year - 1
  772.             else:
  773.                 leap_count = 0
  774.                 check = check - 366
  775.                 year = year - 1
  776.         return None
  777.        
  778.     ####################################################################
  779.     # checks if rhythmbox is already running and in case gives a warning
  780.     ####################################################################
  781.    
  782.     def check_rhythmbox(self, exit_if):
  783.         output = commands.getoutput("ps -A")
  784.         if "rhythmbox" in output:
  785.             if exit_if:
  786.                 confirm_text = "Rhythmbos is currently running. Please close it and then launch fixrhygtk!"
  787.                 confirm_dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, confirm_text)
  788.                 confirm_dialog.set_transient_for(self.main_window)
  789.                 response = confirm_dialog.run()
  790.                 confirm_dialog.destroy()
  791.                 sys.exit(1)
  792.             else:
  793.                 confirm_text = "Rhythmbos is currently running."
  794.                 confirm_text = confirm_text + " If you go on you may corrupt the database or loose all the changes. Do you want to continue?"
  795.                 confirm_dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, confirm_text)
  796.                 confirm_dialog.set_transient_for(self.main_window)
  797.                 response = confirm_dialog.run()
  798.                 confirm_dialog.destroy()
  799.                 if Gtk.ResponseType(response) is not Gtk.ResponseType.YES:
  800.                     sys.exit(1)
  801.                    
  802.     ####################################################################
  803.     # Converts duration if min/sec
  804.     ####################################################################
  805.    
  806.     def duration_convert(self, _duration):
  807.         duration = int(_duration)
  808.         hours = int(duration/3600)
  809.         duration = duration - hours*3600
  810.         minutes = int(duration/60)
  811.         seconds = duration - minutes*60
  812.         if hours > 0:
  813.             d = '%dh ' % hours
  814.         else:
  815.             d = ''
  816.         if minutes > 0:
  817.             d = d + '%dm ' % minutes
  818.         else:
  819.             d = d + ''
  820.         d = d + '%ds' % seconds
  821.         return d
  822.  
  823. ########################################################################
  824. # App initialisation
  825. ########################################################################
  826.  
  827. import sys
  828. import getopt
  829. import shutil
  830. import os
  831. import threading
  832. import commands
  833. import xml.etree.cElementTree as ET
  834.  
  835. reload(sys)
  836. sys.setdefaultencoding( "UTF-8" )
  837. print "fixrhygtk ", VERSION
  838.  
  839. try:
  840.     import gi
  841.     gi.require_version('Gtk', '3.0')
  842. except:
  843.     print "Cannot import gi :("
  844.     pass
  845. try:
  846.     from gi.repository import Gtk, Gdk, Gio
  847. except:
  848.     print "Cannot import gtk.glade :("
  849.     sys.exit(1)
  850.    
  851. if __name__ == "__main__":
  852.     frg = FixRhyGtk(sys.argv[1:])
  853.     Gtk.main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement