def __init__(self): TreeView.__init__(self, enable_drag_drop=False, enable_multiple_select=True) targets = [("text/deepin-songs", gtk.TARGET_SAME_APP, 1), ("text/uri-list", 0, 2), ("text/plain", 0, 3)] self.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_DROP, targets, gtk.gdk.ACTION_COPY) self.pl = None self.add_song_cache = [] sort_key = ["file", "album", "genre", "#track", "artist", "title", "#playcount", "#added"] self.sort_reverse = {key : False for key in sort_key } self.connect_after("drag-data-received", self.on_drag_data_received) self.connect("double-click-item", self.double_click_item_cb) self.connect("button-press-event", self.button_press_cb) self.connect("delete-select-items", self.try_emit_empty_signal) self.connect("motion-notify-item", self.on_motion_notify_item) self.connect("press-return", self.on_press_return) self.draw_area.connect("leave-notify-event", self.on_leave_notify_event) self.set_hide_columns([1]) self.set_expand_column(None) MediaDB.connect("removed", self.__remove_songs) MediaDB.connect("simple-changed", self.__songs_changed) self.song_notify = SongNotify() self.notify_timeout_id = None self.notify_timeout = 400 # ms self.delay_notify_item = None self.notify_offset_x = 5 self.invaild_items = set()
class SongView(TreeView): ''' song view. ''' __gsignals__ = { "begin-add-items" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), "empty-items" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()) } def __init__(self): TreeView.__init__(self, enable_drag_drop=False, enable_multiple_select=True) targets = [("text/deepin-songs", gtk.TARGET_SAME_APP, 1), ("text/uri-list", 0, 2), ("text/plain", 0, 3)] self.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_DROP, targets, gtk.gdk.ACTION_COPY) self.pl = None self.add_song_cache = [] sort_key = ["file", "album", "genre", "#track", "artist", "title", "#playcount", "#added"] self.sort_reverse = {key : False for key in sort_key } self.connect_after("drag-data-received", self.on_drag_data_received) self.connect("double-click-item", self.double_click_item_cb) self.connect("button-press-event", self.button_press_cb) self.connect("delete-select-items", self.try_emit_empty_signal) self.connect("motion-notify-item", self.on_motion_notify_item) self.connect("press-return", self.on_press_return) self.draw_area.connect("leave-notify-event", self.on_leave_notify_event) self.set_hide_columns([1]) self.set_expand_column(None) MediaDB.connect("removed", self.__remove_songs) MediaDB.connect("simple-changed", self.__songs_changed) self.song_notify = SongNotify() self.notify_timeout_id = None self.notify_timeout = 400 # ms self.delay_notify_item = None self.notify_offset_x = 5 self.invaild_items = set() @property def items(self): return self.get_items() def try_emit_empty_signal(self, widget, items): if len(self.items) <= 0: self.emit("empty-items") self.song_notify.hide_notify() try: self.category_view.save_to_library() except: pass def set_current_source(self): if Player.get_source() != self: Player.set_source(self) Dispatcher.emit("save_current_list") def on_press_return(self, widget, items): if items: self.double_click_item_cb(widget, items[0], 0, 0, 0) def double_click_item_cb(self, widget, item, colume, x, y): if item: song = item.get_song() if song.exists(): self.set_highlight_item(item) Player.play_new(item.get_song(), seek=item.get_song().get("seek", None)) self.set_current_source() self.async_reset_error_items() def draw_mask(self, cr, x, y, width, height): draw_alpha_mask(cr, x, y, width, height, "layoutMiddle") def get_songs(self): songs = [] for song_item in self.items: songs.append(song_item.get_song()) return songs def is_empty(self): return len(self.items) == 0 def get_loop_mode(self): return config.get("setting", "loop_mode") def set_loop_mode(self, value): config.set("setting", "loop_mode", value) def get_previous_song(self): if self.is_empty(): if config.get("setting", "empty_random") == "true": return MediaDB.get_random_song("local") else: valid_items = self.get_valid_items() if not valid_items: return None if config.get("setting", "loop_mode") == "random_mode": return self.get_random_song() if self.highlight_item != None: if self.highlight_item in valid_items: current_index = valid_items.index(self.highlight_item) prev_index = current_index - 1 if prev_index < 0: prev_index = len(valid_items) - 1 highlight_item = valid_items[prev_index] else: highlight_item = valid_items[0] self.set_highlight_item(highlight_item) self.visible_highlight() return highlight_item.get_song() def get_next_song(self, manual=False): if self.is_empty(): if config.getboolean("setting", "empty_random"): return MediaDB.get_random_song("local") else: if manual: if config.get("setting", "loop_mode") != "random_mode": return self.get_manual_song() else: return self.get_random_song() elif config.get("setting", "loop_mode") == "list_mode": return self.get_manual_song() elif config.get("setting", "loop_mode") == "order_mode": return self.get_order_song() elif config.get("setting", "loop_mode") == "single_mode": if self.highlight_item != None: return self.highlight_item.get_song() elif config.get("setting", "loop_mode") == "random_mode": return self.get_random_song() def get_order_song(self): valid_items = self.get_valid_items() if not valid_items: return None if self.highlight_item: if self.highlight_item in valid_items: current_index = valid_items.index(self.highlight_item) next_index = current_index + 1 if next_index <= len(valid_items) -1: highlight_item = valid_items[next_index] self.set_highlight_item(highlight_item) return highlight_item.get_song() return None def reset_error_items(self): del self.select_rows[:] self.queue_draw() for each_item in self.items: if each_item in self.invaild_items: continue if each_item.exists(): each_item.clear_error() else: each_item.set_error() async_reset_error_items = property(lambda self: utils.threaded(self.reset_error_items)) def set_song_items(self, items): self.add_items(items, clear_first=True) self.update_item_index() self.update_vadjustment() def get_manual_song(self): valid_items = self.get_valid_items() if not valid_items: return None if self.highlight_item: if self.highlight_item in valid_items: current_index = valid_items.index(self.highlight_item) next_index = current_index + 1 if next_index > len(valid_items) - 1: next_index = 0 highlight_item = valid_items[next_index] else: highlight_item = valid_items[0] else: highlight_item = valid_items[0] self.set_highlight_item(highlight_item) self.visible_highlight() return highlight_item.get_song() def add_invaild_song(self, song): item = SongItem(song) if item in self.items: vaild_item = self.items[self.items.index(item)] vaild_item.set_error() self.invaild_items.add(vaild_item) def get_valid_songs(self): songs = [] for item in self.get_valid_sitems(): songs.append(item.get_song()) return songs def get_random_song(self): valid_items = self.get_valid_items() if not valid_items: return None if self.highlight_item in valid_items: current_index = [valid_items.index(self.highlight_item)] else: current_index = [0] items_index = set(range(len(valid_items))) remaining = items_index.difference(current_index) if len(remaining) <= 0: remaining = [0] highlight_item = valid_items[random.choice(list(remaining))] self.set_highlight_item(highlight_item) self.visible_highlight() return highlight_item.get_song() def get_valid_items(self): self.reset_error_items() return [item for item in self.items if not item.is_error()] def add_songs(self, songs, pos=None, sort=False, play=False): '''Add song to songlist.''' if not songs: return if not isinstance(songs, (list, tuple, set)): songs = [ songs ] song_items = [ SongItem(song) for song in songs if song not in self.get_songs()] if song_items: if not self.items: self.emit_add_signal() self.add_items(song_items, pos, False) if len(songs) >= 1 and play: if songs[0].exists(): del self.select_rows[:] self.queue_draw() self.set_highlight_song(songs[0]) Player.play_new(self.highlight_item.get_song(), seek=self.highlight_item.get_song().get("seek", 0)) self.set_current_source() self.async_reset_error_items() def play_song(self, song, play=False, seek=None): self.async_reset_error_items() highlight_song_flag = self.set_highlight_song(song) if highlight_song_flag: Player.set_song(song, play, seek=seek) def emit_add_signal(self): self.emit("begin-add-items") def play_uris(self, uris, pos=None, sort=True): # self.get_toplevel().window.set_cursor(None) songs = [] for uri in uris: db_songs = MediaDB.get_songs_by_uri(uri) if db_songs: songs.extend(db_songs) if not songs: return if sort: songs.sort() self.add_songs(songs, pos, sort, True) def add_uris(self, uris, pos=None, sort=True): if uris == None: return if not isinstance(uris, (tuple, list, set)): uris = [ uris ] uris = [ utils.get_uri_from_path(uri) for uri in uris ] utils.ThreadLoad(self.load_taginfo, uris, pos, sort).start() # self.load_taginfo(uris, pos, sort) def load_taginfo(self, uris, pos=None, sort=True): start = time.time() if pos is None: pos = len(self.items) for uri in uris: songs = MediaDB.get_songs_by_uri(uri) if not songs: continue self.add_song_cache.extend(songs) end = time.time() if end - start > 0.2: self.render_song(self.add_song_cache, pos, sort) pos += len(self.add_song_cache) del self.add_song_cache[:] start = time.time() if self.add_song_cache: self.render_song(self.add_song_cache, pos, sort) del self.add_song_cache[:] # save playlists try: self.category_view.save_to_library() except: pass @post_gui def render_song(self, songs, pos, sort): if songs: self.add_songs(songs, pos, sort) def get_current_song(self): return self.highlight_item.get_song() def random_reorder(self, *args): with self.keep_select_status(): random.shuffle(self.items) self.update_item_index() self.queue_draw() def set_highlight_song(self, song): if not song: return False if SongItem(song) in self.items: self.set_highlight_item(self.items[self.items.index(SongItem(song))]) self.visible_highlight() self.queue_draw() return True return False def play_select_item(self): if len(self.select_rows) > 0: select_item = self.items[self.select_rows[0]] if select_item.exists(): self.highlight_item = self.items[self.select_rows[0]] Player.play_new(self.highlight_item.get_song(), seek=self.highlight_item.get_song().get("seek", None)) self.set_current_source() def remove_select_items(self): self.delete_select_items() return True def erase_items(self): self.clear() self.emit("empty-items") return True def songs_convert(self): if len(self.select_rows) > 0: songs = [ self.items[self.select_rows[index]].get_song() for index in range(0, len(self.select_rows))] try: AttributesUI(songs).show_window() except: pass def open_song_dir(self): if len(self.select_rows) > 0: song = self.items[self.select_rows[0]].get_song() utils.open_file_directory(song.get_path()) def open_song_editor(self): if len(self.select_rows) > 0: index = self.select_rows[0] select_item = self.items[index] if select_item.exists(): SongEditor([select_item.get_song()]).show_all() def move_to_trash(self): flag = False if len(self.select_rows) > 0: songs = [ self.items[self.select_rows[index]].get_song() for index in range(0, len(self.select_rows))] if self.highlight_item and self.highlight_item.get_song() in songs: Player.stop() self.highlight_item = None flag = True MediaDB.remove(songs) [ utils.move_to_trash(song.get("uri")) for song in songs if song.get_type() != "cue"] self.delete_select_items() if flag: Player.next() return True def try_move_trash(self): ConfirmDialog(_("Prompt"), _("Are you sure to delete?"), confirm_callback=lambda : self.move_to_trash()).show_all() def on_drag_data_received(self, widget, context, x, y, selection, info, timestamp): root_y = widget.allocation.y + y try: pos = self.get_coordinate_row(root_y) except: pos = None if pos == None: pos = len(self.items) if selection.target in ["text/uri-list", "text/plain", "text/deepin-songs"]: if selection.target == "text/deepin-songs" and selection.data: self.add_uris(selection.data.splitlines(), pos, False) elif selection.target == "text/uri-list": selected_uris = selection.get_uris() if len(selected_uris) == 1 and os.path.isdir(utils.get_path_from_uri(selected_uris[0])): self.recursion_add_dir(utils.get_path_from_uri(selected_uris[0])) else: utils.async_parse_uris(selection.get_uris(), True, False, self.add_uris, pos) elif selection.target == "text/plain": raw_path = selection.data path = eval("u" + repr(raw_path).replace("\\\\", "\\")) utils.async_get_uris_from_plain_text(path, self.add_uris, pos) def set_sort_keyword(self, keyword, reverse=False): with self.keep_select_status(): reverse = self.sort_reverse[keyword] items = sorted(self.items, key=lambda item: item.get_song().get_sortable(keyword), reverse=reverse) self.add_items(items, clear_first=True) self.sort_reverse[keyword] = not reverse self.update_item_index() if self.highlight_item != None: self.visible_highlight() self.queue_draw() def get_playmode_menu(self, pos=[], align=False): mode_dict = OrderedDict() mode_dict["single_mode"] = _("Repeat (single)") mode_dict["order_mode"] = _("Order play") mode_dict["list_mode"] = _("Repeat (list)") mode_dict["random_mode"] = _("Randomize") mode_items = [] for key, value in mode_dict.iteritems(): if self.get_loop_mode() == key: tick = (app_theme.get_pixbuf("menu/tick.png"), app_theme.get_pixbuf("menu/tick_press.png"), app_theme.get_pixbuf("menu/tick_disable.png")) mode_items.append((tick, value, self.set_loop_mode, key)) else: tick = None mode_items.append((None, value, self.set_loop_mode, key)) if pos: Menu(mode_items, True).show((pos[0], pos[1])) else: return Menu(mode_items) def button_press_cb(self, widget, event): if event.button == 3 and not self.items: self.popup_add_menu(int(event.x_root), int(event.y_root)) def popup_delete_menu(self, x, y): items = [(None, _("Remove Track from this List"), self.remove_select_items), (None, _("Remove Unavailable Tracks"), self.delete_error_items), (None, _("Move to Trash"), lambda : self.try_move_trash()), (None, _("Clear List"), self.erase_items)] Menu(items, True).show((int(x), int(y))) def delete_error_items(self): self.items = self.get_valid_items() self.update_item_index() self.update_vadjustment() self.queue_draw() def popup_add_menu(self, x, y): menu_items = [ (None, _("URL") , self.add_unknow_uri), (None, _("File"), self.add_file), (None, _("Folder(include subdirectories)"), self.recursion_add_dir), (None, _("Folder"), self.add_dir), ] Menu(menu_items, True).show((x, y)) def get_add_menu(self): menu_items = [ (None, _("File"), self.add_file), (None, _("Folder(include subdirectories)"), self.recursion_add_dir), (None, _("Folder"), self.add_dir), ] return Menu(menu_items) def add_unknow_uri(self, uri=None): def play_or_add_uri(uri): # MediaDB.get_or_create_song({"uri": uri}, "unknown") self.play_uris([uri]) if not uri: input_dialog = InputDialog(_("Add URL"), "", 300, 100, lambda name : play_or_add_uri(name)) input_dialog.show_all() else: play_or_add_uri(uri) def add_file(self, filename=None, play=False): if filename is None: uri = WinFile().run() else: uri = utils.get_uri_from_path(filename) if uri and common.file_is_supported(utils.get_path_from_uri(uri)): try: songs = MediaDB.get_songs_by_uri(uri) except: pass else: self.add_songs(songs, play=play) def add_dir(self): select_dir = WinDir().run() if select_dir: utils.async_parse_uris([select_dir], True, False, self.add_uris) def recursion_add_dir(self, init_dir=None): pos = len(self.items) ImportPlaylistJob(init_dir, self.add_uris, pos, False) def async_add_uris(self, uris, follow_folder=True): if not isinstance(uris, (list, tuple, set)): uris = [ uris ] utils.async_parse_uris(uris, follow_folder, True, self.add_uris) def __songs_changed(self, db, infos): indexs = [] view_songs = self.get_songs() for each_song in infos: if each_song in view_songs: indexs.append(view_songs.index(each_song)) if indexs: for index in indexs: item = self.items[index] item.update(MediaDB.get_song(item.get_song().get("uri")), True) def __remove_songs(self, db, song_type, songs): flag = False if self.highlight_item and self.highlight_item.get_song() in songs: Player.stop() self.highlight_item = None flag = True for song in songs: try: self.items.remove(SongItem(song)) except: pass self.update_item_index() self.update_vadjustment() self.queue_draw() if flag: if len(self.get_valid_items()) > 0: item = self.get_valid_items()[0] self.set_highlight_item(item) Player.play_new(item.get_song(), seek=item.get_song().get("seek", None)) def on_motion_notify_item(self, widget, item, column, item_x, item_y): if item: if self.delay_notify_item is None and self.notify_timeout_id is None: self.delay_notify_item = item self.notify_timeout_id = gobject.timeout_add(self.notify_timeout, self.delay_show_notify, item) else: if self.delay_notify_item != item: self.delay_notify_item = item self.try_to_hide_notify(False) self.notify_timeout_id = gobject.timeout_add(self.notify_timeout, self.delay_show_notify, item) else: self.try_to_hide_notify() def delay_show_notify(self, item): screen_width, screen_height = utils.get_screen_size() view_x, view_y = self.scrolled_window.get_child().get_view_window().get_size() (origin_x, origin_y) = get_widget_root_coordinate(self.draw_area, WIDGET_POS_TOP_LEFT, False) notify_width = self.song_notify.default_width if origin_x + view_x + notify_width + self.notify_offset_x > screen_width: x = origin_x - notify_width - self.notify_offset_x else: x = origin_x + view_x + self.notify_offset_x y = origin_y + self.get_item_offset_y(item) self.song_notify.update_song(item.song) self.song_notify.show(x, y) def on_leave_notify_event(self, widget, event): self.try_to_hide_notify() def try_to_hide_notify(self, hide=True): if self.notify_timeout_id is not None: gobject.source_remove(self.notify_timeout_id) self.notify_timeout_id = None if hide: self.delay_notify_item = None self.song_notify.hide_notify() def get_item_offset_y(self, item): try: index = self.items.index(item) except: index = 0 return item.height * index
class SongView(TreeView): ''' song view. ''' __gsignals__ = { "begin-add-items": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), "empty-items": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()) } def __init__(self): TreeView.__init__(self, enable_drag_drop=False, enable_multiple_select=True) targets = [("text/deepin-songs", gtk.TARGET_SAME_APP, 1), ("text/uri-list", 0, 2), ("text/plain", 0, 3)] self.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_DROP, targets, gtk.gdk.ACTION_COPY) self.pl = None self.add_song_cache = [] sort_key = [ "file", "album", "genre", "#track", "artist", "title", "#playcount", "#added" ] self.sort_reverse = {key: False for key in sort_key} self.connect_after("drag-data-received", self.on_drag_data_received) self.connect("double-click-item", self.double_click_item_cb) self.connect("button-press-event", self.button_press_cb) self.connect("delete-select-items", self.try_emit_empty_signal) self.connect("motion-notify-item", self.on_motion_notify_item) self.connect("press-return", self.on_press_return) self.draw_area.connect("leave-notify-event", self.on_leave_notify_event) self.set_hide_columns([1]) self.set_expand_column(None) MediaDB.connect("removed", self.__remove_songs) MediaDB.connect("simple-changed", self.__songs_changed) self.song_notify = SongNotify() self.notify_timeout_id = None self.notify_timeout = 400 # ms self.delay_notify_item = None self.notify_offset_x = 5 self.invaild_items = set() @property def items(self): return self.get_items() def try_emit_empty_signal(self, widget, items): if len(self.items) <= 0: self.emit("empty-items") self.song_notify.hide_notify() try: self.category_view.save_to_library() except: pass def set_current_source(self): if Player.get_source() != self: Player.set_source(self) Dispatcher.emit("save_current_list") def on_press_return(self, widget, items): if items: self.double_click_item_cb(widget, items[0], 0, 0, 0) def double_click_item_cb(self, widget, item, colume, x, y): if item: song = item.get_song() if song.exists(): self.set_highlight_item(item) Player.play_new(item.get_song(), seek=item.get_song().get("seek", None)) self.set_current_source() self.async_reset_error_items() def draw_mask(self, cr, x, y, width, height): draw_alpha_mask(cr, x, y, width, height, "layoutMiddle") def get_songs(self): songs = [] for song_item in self.items: songs.append(song_item.get_song()) return songs def is_empty(self): return len(self.items) == 0 def get_loop_mode(self): return config.get("setting", "loop_mode") def set_loop_mode(self, value): config.set("setting", "loop_mode", value) def get_previous_song(self): if self.is_empty(): if config.get("setting", "empty_random") == "true": return MediaDB.get_random_song("local") else: valid_items = self.get_valid_items() if not valid_items: return None if config.get("setting", "loop_mode") == "random_mode": return self.get_random_song() if self.highlight_item != None: if self.highlight_item in valid_items: current_index = valid_items.index(self.highlight_item) prev_index = current_index - 1 if prev_index < 0: prev_index = len(valid_items) - 1 highlight_item = valid_items[prev_index] else: highlight_item = valid_items[0] self.set_highlight_item(highlight_item) self.visible_highlight() return highlight_item.get_song() def get_next_song(self, manual=False): if self.is_empty(): if config.getboolean("setting", "empty_random"): return MediaDB.get_random_song("local") else: if manual: if config.get("setting", "loop_mode") != "random_mode": return self.get_manual_song() else: return self.get_random_song() elif config.get("setting", "loop_mode") == "list_mode": return self.get_manual_song() elif config.get("setting", "loop_mode") == "order_mode": return self.get_order_song() elif config.get("setting", "loop_mode") == "single_mode": if self.highlight_item != None: return self.highlight_item.get_song() elif config.get("setting", "loop_mode") == "random_mode": return self.get_random_song() def get_order_song(self): valid_items = self.get_valid_items() if not valid_items: return None if self.highlight_item: if self.highlight_item in valid_items: current_index = valid_items.index(self.highlight_item) next_index = current_index + 1 if next_index <= len(valid_items) - 1: highlight_item = valid_items[next_index] self.set_highlight_item(highlight_item) return highlight_item.get_song() return None def reset_error_items(self): del self.select_rows[:] self.queue_draw() for each_item in self.items: if each_item in self.invaild_items: continue if each_item.exists(): each_item.clear_error() else: each_item.set_error() async_reset_error_items = property( lambda self: utils.threaded(self.reset_error_items)) def set_song_items(self, items): self.add_items(items, clear_first=True) self.update_item_index() self.update_vadjustment() def get_manual_song(self): valid_items = self.get_valid_items() if not valid_items: return None if self.highlight_item: if self.highlight_item in valid_items: current_index = valid_items.index(self.highlight_item) next_index = current_index + 1 if next_index > len(valid_items) - 1: next_index = 0 highlight_item = valid_items[next_index] else: highlight_item = valid_items[0] else: highlight_item = valid_items[0] self.set_highlight_item(highlight_item) self.visible_highlight() return highlight_item.get_song() def add_invaild_song(self, song): item = SongItem(song) if item in self.items: vaild_item = self.items[self.items.index(item)] vaild_item.set_error() self.invaild_items.add(vaild_item) def get_valid_songs(self): songs = [] for item in self.get_valid_sitems(): songs.append(item.get_song()) return songs def get_random_song(self): valid_items = self.get_valid_items() if not valid_items: return None if self.highlight_item in valid_items: current_index = [valid_items.index(self.highlight_item)] else: current_index = [0] items_index = set(range(len(valid_items))) remaining = items_index.difference(current_index) if len(remaining) <= 0: remaining = [0] highlight_item = valid_items[random.choice(list(remaining))] self.set_highlight_item(highlight_item) self.visible_highlight() return highlight_item.get_song() def get_valid_items(self): self.reset_error_items() return [item for item in self.items if not item.is_error()] def add_songs(self, songs, pos=None, sort=False, play=False): '''Add song to songlist.''' if not songs: return if not isinstance(songs, (list, tuple, set)): songs = [songs] song_items = [ SongItem(song) for song in songs if song not in self.get_songs() ] if song_items: if not self.items: self.emit_add_signal() self.add_items(song_items, pos, False) if len(songs) >= 1 and play: if songs[0].exists(): del self.select_rows[:] self.queue_draw() self.set_highlight_song(songs[0]) Player.play_new(self.highlight_item.get_song(), seek=self.highlight_item.get_song().get( "seek", 0)) self.set_current_source() self.async_reset_error_items() def play_song(self, song, play=False, seek=None): self.async_reset_error_items() highlight_song_flag = self.set_highlight_song(song) if highlight_song_flag: Player.set_song(song, play, seek=seek) def emit_add_signal(self): self.emit("begin-add-items") def play_uris(self, uris, pos=None, sort=True): # self.get_toplevel().window.set_cursor(None) songs = [] for uri in uris: db_songs = MediaDB.get_songs_by_uri(uri) if db_songs: songs.extend(db_songs) if not songs: return if sort: songs.sort() self.add_songs(songs, pos, sort, True) def add_uris(self, uris, pos=None, sort=True): if uris == None: return if not isinstance(uris, (tuple, list, set)): uris = [uris] uris = [utils.get_uri_from_path(uri) for uri in uris] utils.ThreadLoad(self.load_taginfo, uris, pos, sort).start() # self.load_taginfo(uris, pos, sort) def load_taginfo(self, uris, pos=None, sort=True): start = time.time() if pos is None: pos = len(self.items) for uri in uris: songs = MediaDB.get_songs_by_uri(uri) if not songs: continue self.add_song_cache.extend(songs) end = time.time() if end - start > 0.2: self.render_song(self.add_song_cache, pos, sort) pos += len(self.add_song_cache) del self.add_song_cache[:] start = time.time() if self.add_song_cache: self.render_song(self.add_song_cache, pos, sort) del self.add_song_cache[:] # save playlists try: self.category_view.save_to_library() except: pass @post_gui def render_song(self, songs, pos, sort): if songs: self.add_songs(songs, pos, sort) def get_current_song(self): return self.highlight_item.get_song() def random_reorder(self, *args): with self.keep_select_status(): random.shuffle(self.items) self.update_item_index() self.queue_draw() def set_highlight_song(self, song): if not song: return False if SongItem(song) in self.items: self.set_highlight_item(self.items[self.items.index( SongItem(song))]) self.visible_highlight() self.queue_draw() return True return False def play_select_item(self): if len(self.select_rows) > 0: select_item = self.items[self.select_rows[0]] if select_item.exists(): self.highlight_item = self.items[self.select_rows[0]] Player.play_new(self.highlight_item.get_song(), seek=self.highlight_item.get_song().get( "seek", None)) self.set_current_source() def remove_select_items(self): self.delete_select_items() return True def erase_items(self): self.clear() self.emit("empty-items") return True def songs_convert(self): if len(self.select_rows) > 0: songs = [ self.items[self.select_rows[index]].get_song() for index in range(0, len(self.select_rows)) ] try: AttributesUI(songs).show_window() except: pass def open_song_dir(self): if len(self.select_rows) > 0: song = self.items[self.select_rows[0]].get_song() utils.open_file_directory(song.get_path()) def open_song_editor(self): if len(self.select_rows) > 0: index = self.select_rows[0] select_item = self.items[index] if select_item.exists(): SongEditor([select_item.get_song()]).show_all() def move_to_trash(self): flag = False if len(self.select_rows) > 0: songs = [ self.items[self.select_rows[index]].get_song() for index in range(0, len(self.select_rows)) ] if self.highlight_item and self.highlight_item.get_song() in songs: Player.stop() self.highlight_item = None flag = True MediaDB.remove(songs) [ utils.move_to_trash(song.get("uri")) for song in songs if song.get_type() != "cue" ] self.delete_select_items() if flag: Player.next() return True def try_move_trash(self): ConfirmDialog( _("Prompt"), _("Are you sure to delete?"), confirm_callback=lambda: self.move_to_trash()).show_all() def on_drag_data_received(self, widget, context, x, y, selection, info, timestamp): root_y = widget.allocation.y + y try: pos = self.get_coordinate_row(root_y) except: pos = None if pos == None: pos = len(self.items) if selection.target in [ "text/uri-list", "text/plain", "text/deepin-songs" ]: if selection.target == "text/deepin-songs" and selection.data: self.add_uris(selection.data.splitlines(), pos, False) elif selection.target == "text/uri-list": selected_uris = selection.get_uris() if len(selected_uris) == 1 and os.path.isdir( utils.get_path_from_uri(selected_uris[0])): self.recursion_add_dir( utils.get_path_from_uri(selected_uris[0])) else: utils.async_parse_uris(selection.get_uris(), True, False, self.add_uris, pos) elif selection.target == "text/plain": raw_path = selection.data path = eval("u" + repr(raw_path).replace("\\\\", "\\")) utils.async_get_uris_from_plain_text(path, self.add_uris, pos) def set_sort_keyword(self, keyword, reverse=False): with self.keep_select_status(): reverse = self.sort_reverse[keyword] items = sorted( self.items, key=lambda item: item.get_song().get_sortable(keyword), reverse=reverse) self.add_items(items, clear_first=True) self.sort_reverse[keyword] = not reverse self.update_item_index() if self.highlight_item != None: self.visible_highlight() self.queue_draw() def get_playmode_menu(self, pos=[], align=False): mode_dict = OrderedDict() mode_dict["single_mode"] = _("Repeat (single)") mode_dict["order_mode"] = _("Order play") mode_dict["list_mode"] = _("Repeat (list)") mode_dict["random_mode"] = _("Randomize") mode_items = [] for key, value in mode_dict.iteritems(): if self.get_loop_mode() == key: tick = (app_theme.get_pixbuf("menu/tick.png"), app_theme.get_pixbuf("menu/tick_press.png"), app_theme.get_pixbuf("menu/tick_disable.png")) mode_items.append((tick, value, self.set_loop_mode, key)) else: tick = None mode_items.append((None, value, self.set_loop_mode, key)) if pos: Menu(mode_items, True).show((pos[0], pos[1])) else: return Menu(mode_items) def button_press_cb(self, widget, event): if event.button == 3 and not self.items: self.popup_add_menu(int(event.x_root), int(event.y_root)) def popup_delete_menu(self, x, y): items = [ (None, _("Remove Track from this List"), self.remove_select_items), (None, _("Remove Unavailable Tracks"), self.delete_error_items), (None, _("Move to Trash"), lambda: self.try_move_trash()), (None, _("Clear List"), self.erase_items) ] Menu(items, True).show((int(x), int(y))) def delete_error_items(self): self.items = self.get_valid_items() self.update_item_index() self.update_vadjustment() self.queue_draw() def popup_add_menu(self, x, y): menu_items = [ (None, _("URL"), self.add_unknow_uri), (None, _("File"), self.add_file), (None, _("Folder (include subdirectories)"), self.recursion_add_dir), (None, _("Folder"), self.add_dir), ] Menu(menu_items, True).show((x, y)) def get_add_menu(self): menu_items = [ (None, _("File"), self.add_file), (None, _("Folder (include subdirectories)"), self.recursion_add_dir), (None, _("Folder"), self.add_dir), ] return Menu(menu_items) def add_unknow_uri(self, uri=None): def play_or_add_uri(uri): # MediaDB.get_or_create_song({"uri": uri}, "unknown") self.play_uris([uri]) if not uri: input_dialog = InputDialog(_("Add URL"), "", 300, 100, lambda name: play_or_add_uri(name)) input_dialog.show_all() else: play_or_add_uri(uri) def add_file(self, filename=None, play=False): if filename is None: uri = WinFile().run() else: uri = utils.get_uri_from_path(filename) if uri and common.file_is_supported(utils.get_path_from_uri(uri)): try: songs = MediaDB.get_songs_by_uri(uri) except: pass else: self.add_songs(songs, play=play) def add_dir(self): select_dir = WinDir().run() if select_dir: utils.async_parse_uris([select_dir], True, False, self.add_uris) def recursion_add_dir(self, init_dir=None): pos = len(self.items) ImportPlaylistJob(init_dir, self.add_uris, pos, False) def async_add_uris(self, uris, follow_folder=True): if not isinstance(uris, (list, tuple, set)): uris = [uris] utils.async_parse_uris(uris, follow_folder, True, self.add_uris) def __songs_changed(self, db, infos): indexs = [] view_songs = self.get_songs() for each_song in infos: if each_song in view_songs: indexs.append(view_songs.index(each_song)) if indexs: for index in indexs: item = self.items[index] item.update(MediaDB.get_song(item.get_song().get("uri")), True) def __remove_songs(self, db, song_type, songs): flag = False if self.highlight_item and self.highlight_item.get_song() in songs: Player.stop() self.highlight_item = None flag = True for song in songs: try: self.items.remove(SongItem(song)) except: pass self.update_item_index() self.update_vadjustment() self.queue_draw() if flag: if len(self.get_valid_items()) > 0: item = self.get_valid_items()[0] self.set_highlight_item(item) Player.play_new(item.get_song(), seek=item.get_song().get("seek", None)) def on_motion_notify_item(self, widget, item, column, item_x, item_y): if item: if self.delay_notify_item is None and self.notify_timeout_id is None: self.delay_notify_item = item self.notify_timeout_id = gobject.timeout_add( self.notify_timeout, self.delay_show_notify, item) else: if self.delay_notify_item != item: self.delay_notify_item = item self.try_to_hide_notify(False) self.notify_timeout_id = gobject.timeout_add( self.notify_timeout, self.delay_show_notify, item) else: self.try_to_hide_notify() def delay_show_notify(self, item): screen_width, screen_height = utils.get_screen_size() view_x, view_y = self.scrolled_window.get_child().get_view_window( ).get_size() (origin_x, origin_y) = get_widget_root_coordinate(self.draw_area, WIDGET_POS_TOP_LEFT, False) notify_width = self.song_notify.default_width if origin_x + view_x + notify_width + self.notify_offset_x > screen_width: x = origin_x - notify_width - self.notify_offset_x else: x = origin_x + view_x + self.notify_offset_x y = origin_y + self.get_item_offset_y(item) self.song_notify.update_song(item.song) self.song_notify.show(x, y) def on_leave_notify_event(self, widget, event): self.try_to_hide_notify() def try_to_hide_notify(self, hide=True): if self.notify_timeout_id is not None: gobject.source_remove(self.notify_timeout_id) self.notify_timeout_id = None if hide: self.delay_notify_item = None self.song_notify.hide_notify() def get_item_offset_y(self, item): try: index = self.items.index(item) except: index = 0 return item.height * index