Merge lp:~gero-bare/noise/fix-1181327 into lp:~elementary-apps/noise/trunk

Proposed by Corentin Noël
Status: Rejected
Rejected by: Danielle Foré
Proposed branch: lp:~gero-bare/noise/fix-1181327
Merge into: lp:~elementary-apps/noise/trunk
Diff against target: 417 lines (+295/-1)
7 files modified
core/Settings.vala (+1/-0)
schemas/org.pantheon.noise.gschema.xml (+5/-0)
src/CMakeLists.txt (+1/-0)
src/DataBase.vala (+23/-0)
src/Dialogs/ManageBlackList.vala (+178/-0)
src/Dialogs/PreferencesWindow.vala (+15/-1)
src/LocalBackend/LocalLibrary.vala (+72/-0)
To merge this branch: bzr merge lp:~gero-bare/noise/fix-1181327
Reviewer Review Type Date Requested Status
elementary Apps team Pending
elementary UX Pending
Review via email: mp+310838@code.launchpad.net

This proposal supersedes a proposal from 2015-11-11.

Description of the change

I'm implemented a blacklist system.
Basically you can add folders to being ignored,
or every file deleted can be blacklisted.

For now I want to hear some UX feedback.

To post a comment you must log in.
Revision history for this message
Viko Adi Rahmawan (vikoadi) wrote : Posted in a previous version of this proposal

Do we really need blacklisting here?
I thought removing song should just move it to trash (and it should be renamed as 'Move to Trash' or something)

Revision history for this message
Gero.Bare (gero-bare) wrote : Posted in a previous version of this proposal

Sorry, first all I just word it badly.

The blacklist should be put in files removed from the library not deleted.

Second, I forgot to add the proper check.

Also I should notify the user that there are files/folders being omitted due to the blacklist.

Revision history for this message
Corentin Noël (tintou) wrote : Posted in a previous version of this proposal

Synced with trunk here lp:~tintou/noise/fix-1181327

Revision history for this message
Sam Hewitt (snwh) wrote :

Blacklisting only would exempt files from the auto-import process. The "blacklist" is in the event of unwanted items being auto-imported.

All the "blacklist" is, is the list any and all files that a user has manually removed from his/her library.

I.e. when a user hits "Remove from Library" on an item in their Library, it's added to the blacklist (along with being removed) and to remove said item from the blacklist all they needs to do is re-import it from the gear menu.

Also, this shouldn't require any new user interface elements.

Revision history for this message
Gero.Bare (gero-bare) wrote :

@snwm
So you want to blacklist files so they won't be added again in background re-scans, but when you fully re-import all the library remove them from blacklist? Is that correct?

Revision history for this message
Zisu Andrei (matzipan) wrote :

Hey Gero,

I think if you re-import the entire library, the blacklist in that library should still be applicable.

The only time when a blacklist item is remove is when it is _directly_ added imported.

Unmerged revisions

2004. By Gero.Bare

Fixed some style isues.
Now the user will see listed paths instead of uris because is more intuitive, the backend are uris, but it transparent to the user.

2003. By Gero.Bare

Add blacklist system

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'core/Settings.vala'
2--- core/Settings.vala 2015-10-14 20:56:51 +0000
3+++ core/Settings.vala 2016-11-15 06:21:53 +0000
4@@ -65,6 +65,7 @@
5 public bool update_folder_hierarchy { get; set; }
6 public bool write_metadata_to_file { get; set; }
7 public bool copy_imported_music { get; set; }
8+ public bool blacklist_on_removal { get; set; }
9 public bool close_while_playing { get; set; }
10 public int64 last_media_playing { get; set; }
11 public string last_playlist_playing { get; set; }
12
13=== modified file 'schemas/org.pantheon.noise.gschema.xml'
14--- schemas/org.pantheon.noise.gschema.xml 2015-08-24 16:05:21 +0000
15+++ schemas/org.pantheon.noise.gschema.xml 2016-11-15 06:21:53 +0000
16@@ -41,6 +41,11 @@
17 <summary>Whether or not to copy imported files to music folder</summary>
18 <description>Whether or not to copy imported files to music folder</description>
19 </key>
20+ <key type="b" name="blacklist-on-removal">
21+ <default>false</default>
22+ <summary>Whether or not to blacklist files on removal from the library</summary>
23+ <description>Whether or not to blacklist files on removal from the library</description>
24+ </key>
25 <key type="b" name="close-while-playing">
26 <default>false</default>
27 <summary>Whether to close the player's window instead of hiding/minimizing it when a song is still playing. The window can be brought back by activating its desktop file.</summary>
28
29=== modified file 'src/CMakeLists.txt'
30--- src/CMakeLists.txt 2016-02-08 16:56:22 +0000
31+++ src/CMakeLists.txt 2016-11-15 06:21:53 +0000
32@@ -66,6 +66,7 @@
33 Dialogs/SetMusicFolderConfirmation.vala
34 Dialogs/TransferFromDeviceDialog.vala
35 Dialogs/SyncWarningDialog.vala
36+ Dialogs/ManageBlackList.vala
37 )
38
39 set (CLIENT_VALAC_OPTIONS
40
41=== modified file 'src/DataBase.vala'
42--- src/DataBase.vala 2016-09-29 14:43:39 +0000
43+++ src/DataBase.vala 2016-11-15 06:21:53 +0000
44@@ -97,6 +97,11 @@
45 public const string COLUMNS = "+3";
46 }
47
48+ namespace Blacklists {
49+ public const string TABLE_NAME = "blacklists";
50+ public const string URI = "+0";
51+ }
52+
53 public static GLib.Value? query_field (int64 rowid, Gda.Connection connection, string table, string field) {
54 try {
55 var sql = new Gda.SqlBuilder (Gda.SqlStatementType.SELECT);
56@@ -362,5 +367,23 @@
57 }
58 }
59 }
60+
61+ /*
62+ * Creating the blacklist of items
63+ */
64+ operation = Gda.ServerOperation.prepare_create_table (connection, "blacklists", e,
65+ "uri", typeof (string), Gda.ServerOperationCreateTableFlag.UNIQUE_FLAG);
66+ if (e != null) {
67+ critical (e.message);
68+ } else {
69+ try {
70+ operation.perform_create_table ();
71+ } catch (Error e) {
72+ // e.code == 1 is when the table already exists.
73+ if (e.code != 1) {
74+ critical (e.message);
75+ }
76+ }
77+ }
78 }
79 }
80
81=== added file 'src/Dialogs/ManageBlackList.vala'
82--- src/Dialogs/ManageBlackList.vala 1970-01-01 00:00:00 +0000
83+++ src/Dialogs/ManageBlackList.vala 2016-11-15 06:21:53 +0000
84@@ -0,0 +1,178 @@
85+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
86+/*-
87+ * Copyright (c) 2015 Noise Developers (http://launchpad.net/noise)
88+ *
89+ * This library is free software; you can redistribute it and/or
90+ * modify it under the terms of the GNU Library General Public
91+ * License as published by the Free Software Foundation; either
92+ * version 2 of the License, or (at your option) any later version.
93+ *
94+ * This library is distributed in the hope that it will be useful,
95+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
96+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
97+ * Library General Public License for more details.
98+ *
99+ * You should have received a copy of the GNU Library General Public
100+ * License along with this library; if not, write to the
101+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
102+ * Boston, MA 02111-1307, USA.
103+ *
104+ * The Noise authors hereby grant permission for non-GPL compatible
105+ * GStreamer plugins to be used and distributed together with GStreamer
106+ * and Noise. This permission is above and beyond the permissions granted
107+ * by the GPL license by which Noise is covered. If you modify this code
108+ * you may extend this exception to your version of the code, but you are not
109+ * obligated to do so. If you do not wish to do so, delete this exception
110+ * statement from your version.
111+ * Authored by: Geronimo Bareiro <gero.bare@gmail.com>
112+ */
113+
114+public class Noise.ManageBlackList : Gtk.Dialog {
115+ public const int MIN_WIDTH = 420;
116+ public const int MIN_HEIGHT = 300;
117+
118+ private Gtk.TreeView tree_view;
119+ private Gtk.ListStore liststore;
120+
121+ private Gtk.Button add_folder_button;
122+ private Gtk.Button remove_button;
123+ private Gtk.Button close_button;
124+
125+ private Noise.LocalLibrary library_manager { get { return (Noise.LocalLibrary) libraries_manager.local_library; } }
126+
127+ public ManageBlackList () {}
128+
129+ construct {
130+ title = _("Blacklisted Items");
131+ set_size_request (MIN_WIDTH, MIN_HEIGHT);
132+ resizable = false;
133+ deletable = false;
134+ destroy_with_parent = true;
135+ window_position = Gtk.WindowPosition.CENTER;
136+ set_transient_for (App.main_window);
137+
138+ add_folder_button = new Gtk.Button.with_label (_("Add Folders"));
139+
140+ remove_button = new Gtk.Button.with_label (_("Remove"));
141+ remove_button.sensitive = false;
142+
143+ liststore = new Gtk.ListStore (1, typeof (string));
144+
145+ tree_view = new Gtk.TreeView ();
146+ tree_view.set_model (liststore);
147+ tree_view.hexpand = true;
148+ tree_view.headers_visible = false;
149+ tree_view.insert_column_with_data_func (-1, "", new Gtk.CellRendererText (), (column, cell, model, iter) => {
150+ string uri;
151+ model.get(iter, 0, out uri);
152+ try {
153+ (cell as Gtk.CellRendererText).text = GLib.Filename.from_uri (uri);
154+ } catch (Error e) {
155+ warning (e.message);
156+ (cell as Gtk.CellRendererText).text = uri;
157+ }
158+ });
159+ tree_view.get_selection ().set_mode (Gtk.SelectionMode.MULTIPLE);
160+
161+ var scrolled_window = new Gtk.ScrolledWindow (null, null);
162+ scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC , Gtk.PolicyType.AUTOMATIC);
163+ scrolled_window.add (tree_view);
164+ scrolled_window.set_min_content_height (MIN_HEIGHT - 50);
165+
166+ var list_grid = new Gtk.Grid ();
167+ list_grid.attach (scrolled_window, 0, 0, 3, 3);
168+ list_grid.attach (add_folder_button, 1, 3, 1, 1);
169+ list_grid.attach (remove_button, 2, 3, 1, 1);
170+
171+ close_button = new Gtk.Button.with_label (_("Close"));
172+
173+ var button_box = new Gtk.ButtonBox (Gtk.Orientation.HORIZONTAL);
174+ button_box.set_layout (Gtk.ButtonBoxStyle.END);
175+ button_box.pack_end (close_button);
176+ button_box.margin_right = 12;
177+ button_box.margin_top = 12;
178+
179+ Gtk.Grid main_grid = new Gtk.Grid ();
180+ main_grid.attach (list_grid, 0, 0, 4, 4);
181+ main_grid.attach (button_box, 3, 4, 1, 1);
182+
183+ ((Gtk.Container) get_content_area ()).add (main_grid);
184+
185+ load_blacklist ();
186+ connect_signals ();
187+ }
188+
189+ private void load_blacklist () {
190+ debug ("Loading blacklist");
191+ liststore.clear ();
192+ var black_list = library_manager.get_blacklist ();
193+ foreach (string uri in black_list) {
194+ debug ("Uri: %s", uri);
195+ Gtk.TreeIter iter;
196+ liststore.append (out iter);
197+ liststore.set (iter, 0, uri);
198+ }
199+ }
200+
201+ private void connect_signals () {
202+ close_button.clicked.connect (() => { destroy ();});
203+
204+ add_folder_button.clicked.connect (on_add);
205+
206+ remove_button.clicked.connect (on_remove);
207+
208+ tree_view.get_selection ().changed.connect (on_selection_changed);
209+ }
210+
211+ private void on_selection_changed () {
212+ if (tree_view.get_selection ().count_selected_rows () > 1) {
213+ remove_button.sensitive = true;
214+ } else {
215+ remove_button.sensitive = true;
216+ }
217+
218+ }
219+
220+ private void on_add () {
221+ debug ("Adding folders to blacklist");
222+
223+ var file_chooser = new Gtk.FileChooserDialog (_("Folders to blacklist"), this,
224+ Gtk.FileChooserAction.SELECT_FOLDER,
225+ _("Cancel"), Gtk.ResponseType.CANCEL,
226+ _("Open"), Gtk.ResponseType.ACCEPT);
227+ file_chooser.set_select_multiple (true);
228+ file_chooser.set_local_only (true);
229+ if (file_chooser.run () == Gtk.ResponseType.ACCEPT) {
230+ foreach (var folder in file_chooser.get_files ()) {
231+ library_manager.blacklist_uri (folder.get_uri ());
232+ }
233+ }
234+
235+ file_chooser.destroy ();
236+
237+ // Reload after we finished.
238+ load_blacklist ();
239+ }
240+
241+ private void on_remove () {
242+ debug ("BlactList on_remove");
243+ List<Gtk.TreeIter?> items_to_remove = new List<Gtk.TreeIter?>();
244+ var list = tree_view.get_selection ().get_selected_rows (null);
245+ debug ("List lenght %u", list.length ());
246+
247+ foreach (unowned Gtk.TreePath element in list) {
248+ Gtk.TreeIter iter;
249+ if (liststore.get_iter (out iter, element)) {
250+ GLib.Value uri;
251+ liststore.get_value (iter, 0, out uri);
252+ debug ("Appending uri to remove: %s", (string)uri);
253+ library_manager.unblacklist_uri ((string)uri);
254+ items_to_remove.append (iter);
255+ }
256+ }
257+
258+ foreach (var element in items_to_remove) {
259+ liststore.remove (element);
260+ }
261+ }
262+}
263
264=== modified file 'src/Dialogs/PreferencesWindow.vala'
265--- src/Dialogs/PreferencesWindow.vala 2015-12-19 14:41:06 +0000
266+++ src/Dialogs/PreferencesWindow.vala 2016-11-15 06:21:53 +0000
267@@ -124,6 +124,8 @@
268 private Gtk.Switch write_file_metadata_switch;
269 private Gtk.Switch copy_imported_music_switch;
270 private Gtk.Switch hide_on_close_switch;
271+ private Gtk.Switch blacklist_on_removal_switch;
272+ private Gtk.Button manage_blacklist_button;
273 public Noise.SettingsWindow.NoteBook_Page page;
274
275 public GeneralPage (Gtk.FileChooserButton library_filechooser) {
276@@ -158,7 +160,19 @@
277 copy_imported_music_switch = new Gtk.Switch ();
278 main_settings.schema.bind("copy-imported-music", copy_imported_music_switch, "active", SettingsBindFlags.DEFAULT);
279 page.add_option (new Gtk.Label (_("Copy imported files to Library:")), copy_imported_music_switch, ref row);
280-
281+
282+ blacklist_on_removal_switch = new Gtk.Switch ();
283+ main_settings.schema.bind("blacklist-on-removal", blacklist_on_removal_switch, "active", SettingsBindFlags.DEFAULT);
284+ page.add_option (new Gtk.Label (_("Blacklist files on removal:")), blacklist_on_removal_switch, ref row);
285+
286+ manage_blacklist_button = new Gtk.Button.with_label (_("Manage"));
287+ manage_blacklist_button.clicked.connect (() => {
288+ var dialog = new Noise.ManageBlackList ();
289+ dialog.show_all ();
290+ dialog.run();
291+ });
292+ page.add_option (new Gtk.Label (_("Blacklisted items:")), manage_blacklist_button, ref row);
293+
294 label = new Gtk.Label (_("Desktop Integration"));
295 page.add_section (label, ref row);
296
297
298=== modified file 'src/LocalBackend/LocalLibrary.vala'
299--- src/LocalBackend/LocalLibrary.vala 2016-09-29 15:00:27 +0000
300+++ src/LocalBackend/LocalLibrary.vala 2016-11-15 06:21:53 +0000
301@@ -44,6 +44,7 @@
302 private Gee.HashMap<int64?, Media> _medias;
303 private Gee.TreeSet<Media> _searched_medias;
304 private Gee.HashMap<uint, Album> album_info;
305+ private Gee.TreeSet<string> _blacklisted_uris;
306
307 public StaticPlaylist p_music;
308
309@@ -114,6 +115,9 @@
310 var p = new LocalStaticPlaylist (p_id, connection);
311 _playlists.add (p);
312 }
313+
314+ //Load all blacklisted items from database
315+ _blacklisted_uris = get_blacklisted_uris_from_database ();
316 }
317
318 /*
319@@ -229,11 +233,27 @@
320
321 private async void add_folder_to_library_async (Gee.Collection<string> folders) {
322 var files = new Gee.TreeSet<string> ();
323+
324+ // We Transform uris to paths
325+ var paths_blacklisted = new Gee.ArrayList<string> ();
326+ foreach (var item in _blacklisted_uris) {
327+ try {
328+ paths_blacklisted.add (GLib.Filename.from_uri (item));
329+ } catch (Error e) {
330+ warning (e.message);
331+ }
332+ }
333+ // We exclude blacklisted paths
334+ folders.remove_all_array (paths_blacklisted.to_array ());
335+
336 foreach (var folder in folders) {
337 var file = File.new_for_path (folder);
338 FileUtils.count_music_files (file, files);
339 }
340
341+ // We exclude blacklisted files
342+ files.remove_all_array (_blacklisted_uris.to_array ());
343+
344 foreach (var m in get_medias ()) {
345 if (files.contains (m.uri))
346 files.remove (m.uri);
347@@ -820,6 +840,10 @@
348 foreach (var m in toRemove) {
349 try {
350 connection.delete_row_from_table (Database.Media.TABLE_NAME, "rowid", m.rowid);
351+ if (Settings.Main.get_default ().blacklist_on_removal && !trash) {
352+ blacklist_uri (m.uri);
353+ }
354+
355 } catch (Error e) {
356 critical (e.message);
357 }
358@@ -878,6 +902,35 @@
359 }
360 }
361
362+ public void blacklist_uri (string uri) {
363+ if (_blacklisted_uris.contains (uri)) {
364+ return;
365+ }
366+
367+ try {
368+ var builder = new Gda.SqlBuilder (Gda.SqlStatementType.INSERT);
369+ builder.set_table (Database.Blacklists.TABLE_NAME);
370+ builder.add_field_value_as_gvalue ("uri", uri);
371+ connection.statement_execute_non_select (builder.get_statement (), null, null);
372+ _blacklisted_uris.add (uri);
373+ } catch (Error e) {
374+ critical (e.message);
375+ }
376+ }
377+
378+ public void unblacklist_uri (string uri) {
379+ try {
380+ connection.delete_row_from_table (Database.Blacklists.TABLE_NAME, "uri", uri);
381+ _blacklisted_uris.remove (uri);
382+ } catch (Error e) {
383+ critical (e.message);
384+ }
385+ }
386+
387+ public string[] get_blacklist () {
388+ return _blacklisted_uris.to_array ();
389+ }
390+
391 private Gee.Collection<int64?> get_rowids_from_table (string table_name) {
392 var ids = new Gee.TreeSet<int64?> ();
393 try {
394@@ -896,4 +949,23 @@
395
396 return ids;
397 }
398+
399+ private Gee.TreeSet<string> get_blacklisted_uris_from_database () {
400+ var blacklisted_uris = new Gee.TreeSet<string> ();
401+ try {
402+ var builder = new Gda.SqlBuilder (Gda.SqlStatementType.SELECT);
403+ builder.select_add_target (Database.Blacklists.TABLE_NAME, null);
404+ builder.select_add_field ("uri", null, null);
405+ var data_model = connection.statement_execute_select (builder.get_statement (), null);
406+ for (int i = 0; i < data_model.get_n_rows (); i++) {
407+ var uri = data_model.get_value_at (data_model.get_column_index ("uri"), i);
408+ blacklisted_uris.add (uri.dup_string ());
409+ }
410+ } catch (Error e) {
411+ // TODO: Expose errors to the user !
412+ critical ("Could not query table %s : %s", Database.Blacklists.TABLE_NAME, e.message);
413+ }
414+
415+ return blacklisted_uris;
416+ }
417 }

Subscribers

People subscribed via source and target branches