1#!/bin/python
2import gi
3gi.require_version('Gtk', '3.0')
4from gi.repository import Gtk
5from gi.repository import Pango
6import os
7import mimetypes
8import bliss
9import csv
10import sys
11import threading
12
13def analyze(url_lib, url_csv, self):
14    if self.recursive:
15        file_list = [os.path.join(dp, f) for dp, dn, fn in os.walk(os.path.expanduser(url_lib)) for f in fn]
16    else:
17        file_list = [os.path.join(url_lib, f) for f in os.listdir(url_lib) if os.path.isfile(os.path.join(url_lib, f))]
18
19    audio_files = []
20
21    for file_n in file_list:
22        guess = mimetypes.guess_type(file_n)[0]
23        if guess is not None and "audio" in guess:
24            audio_files.append(file_n)
25
26    if not audio_files:
27        print("Please enter a valid directory containing audio files")
28        self.goBtn.set_label("Go")
29        return
30
31
32    self.label_done.hide()
33    self.progressBar.show()
34    self.progressBar.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
35    self.progressBar.set_show_text(True)
36
37    with open(url_csv, "w") as csvfile:
38        library_writer = csv.writer(csvfile, delimiter='|', quotechar="'", quoting=csv.QUOTE_MINIMAL)
39        for i,file_n in enumerate(audio_files):
40            if self.e.isSet():
41                self.e.clear()
42                break
43
44            with bliss.bl_song(file_n) as song:
45                # Test if the file has been decoded properly (ugly)
46                if song["duration"] > 0:
47                    self.progressBar.set_text(song["filename"])
48                    library_writer.writerow((song["filename"], song["album"], song["force_vector"]["attack"], song["force_vector"]["tempo"], song["force_vector"]["amplitude"], song["force_vector"]["frequency"]))
49                    csvfile.flush()
50                else:
51                    print("Couldn't decode file '%s', skipping..." % file_n)
52            self.progressBar.set_fraction(i/(len(audio_files) - 1))
53
54    self.progressBar.hide()
55    self.label_done.set_label("Done!")
56    self.label_done.show()
57    self.goBtn.set_label("Go")
58    print("Scan completed, data is availabe at '%s'" % url_csv)
59
60class MyWindow(Gtk.Window):
61    def __init__(self):
62        Gtk.Window.__init__(self, title="Bliss data generator")
63        self.url_csv = os.path.join(os.getcwd(), "output.csv")
64        self.url_lib = ""
65        self.th = None
66        self.recursive = False
67        self.e = threading.Event()
68        self.progressBar = Gtk.ProgressBar()
69
70        openDirBtn = Gtk.Button.new_with_label("Open...")
71        openDirBtn.connect("clicked", self.onOpenDir)
72
73        saveFileBtn = Gtk.Button.new_with_label("Save as CSV...")
74        saveFileBtn.connect("clicked", self.onSaveFile)
75
76        self.goBtn = Gtk.Button.new_with_label("Go")
77        self.goBtn.connect("clicked", self.go)
78        quitBtn = Gtk.Button.new_with_label("Quit")
79        quitBtn.connect("clicked", self.quit)
80
81        recursiveBtn = Gtk.CheckButton("Recursive scan")
82        recursiveBtn.connect("toggled", self.recursiveCheck)
83
84        self.label_open = Gtk.Label(self.url_lib)
85        self.label_open.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
86        self.label_save = Gtk.Label(self.url_csv)
87        self.label_save.set_ellipsize(Pango.EllipsizeMode.MIDDLE)
88        self.label_done = Gtk.Label()
89
90        sizer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
91        sizer_open = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
92        sizer_save = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
93        sizer_set = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
94
95        sizer.pack_start(sizer_open, True, True, 5)
96        sizer.pack_start(sizer_save, True, True, 5)
97        sizer.pack_start(self.progressBar, True, True, 5)
98        sizer.pack_start(self.label_done, True, True, 5)
99        sizer.pack_start(recursiveBtn, True, True, 5)
100        sizer.pack_start(sizer_set, True, True, 5)
101
102        sizer_open.pack_start(openDirBtn, False, True, 5)
103        sizer_open.pack_start(self.label_open, True, True, 5)
104        sizer_save.pack_start(saveFileBtn, False, True, 5)
105        sizer_save.pack_start(self.label_save, True, True, 5)
106        sizer_set.pack_start(quitBtn, True, True, 5)
107        sizer_set.pack_start(self.goBtn, True, True, 5)
108
109        self.add(sizer)
110
111    def quit(self, evt):
112        Gtk.main_quit()
113
114    def go(self, button):
115        if os.path.isabs(self.url_lib) and os.path.isabs(self.url_csv):
116            if self.th is None or not self.th.isAlive():
117                self.th = threading.Thread(target=analyze, args=(self.url_lib, self.url_csv, self))
118                button.set_label("Cancel")
119                self.th.start()
120            else:
121                button.set_label("Go")
122                self.e.set()
123        else:
124            message = Gtk.MessageDialog(parent=self, flags=0, type=Gtk.MessageType.WARNING,
125                buttons=Gtk.ButtonsType.NONE, message_format=None)
126            message.set_markup("Please enter a valid directory containing audio files")
127            message.show()
128
129    def onOpenDir(self, evt):
130        dialog = Gtk.FileChooserDialog("Please choose a folder to analyze", self,
131            Gtk.FileChooserAction.SELECT_FOLDER,
132            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, "Select", Gtk.ResponseType.OK))
133        dialog.set_default_size(800, 400)
134        response = dialog.run()
135
136        if response == Gtk.ResponseType.OK:
137            self.url_lib = dialog.get_filename()
138            self.label_open.set_label(self.url_lib)
139
140        dialog.destroy()
141
142    def recursiveCheck(self, button):
143        self.recursive = button.get_active()
144
145    def onSaveFile(self, evt):
146        dialog = Gtk.FileChooserDialog("Please choose an output CSV file", self,
147            Gtk.FileChooserAction.SAVE,
148            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, "Select", Gtk.ResponseType.OK))
149        dialog.set_default_size(800, 400)
150        response = dialog.run()
151
152        if response == Gtk.ResponseType.OK:
153            self.url_csv= dialog.get_filename()
154            self.label_save.set_label(self.url_csv)
155
156        dialog.destroy()
157
158win = MyWindow()
159win.connect("delete-event", Gtk.main_quit)
160win.show_all()
161win.progressBar.hide()
162win.label_done.hide()
163
164Gtk.main()
165