]> kaliko git repositories - mpd-sima.git/blob - simadb_cli
Add an undocumented mopidy compatibility option
[mpd-sima.git] / simadb_cli
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 # Copyright (c) 2010-2015 Jack Kaliko <kaliko@azylum.org>
5 #
6 #  This file is part of MPD_sima
7 #
8 #  MPD_sima is free software: you can redistribute it and/or modify
9 #  it under the terms of the GNU General Public License as published by
10 #  the Free Software Foundation, either version 3 of the License, or
11 #  (at your option) any later version.
12 #
13 #  MPD_sima is distributed in the hope that it will be useful,
14 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #  GNU General Public License for more details.
17 #
18 #  You should have received a copy of the GNU General Public License
19 #  along with MPD_sima.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 #
22
23 __version__ = '0.4.1'
24
25 # IMPORT#
26 from argparse import (ArgumentParser, SUPPRESS)
27 from difflib import get_close_matches
28 from locale import getpreferredencoding
29 from os import (environ, chmod, makedirs)
30 from os.path import (join, isdir, isfile, expanduser)
31 from sys import (exit, stdout, stderr)
32
33 from sima.lib.track import Track
34 from sima.utils import utils
35 from sima.lib import simadb
36 from musicpd import MPDClient, MPDError
37
38
39 DESCRIPTION = """
40 simadb_cli helps you to edit entries in your own DB of similarity
41 between artists."""
42 DB_NAME = 'sima.db'
43
44 # Options list
45 # pop out 'sw' value before creating ArgumentParser object.
46 OPTS = list([
47     {
48         'sw':['-d', '--dbfile'],
49         'type': str,
50         'dest':'dbfile',
51         'action': utils.Wfile,
52         'help': 'File to read/write database from/to'},
53     {
54         'sw': ['-S', '--host'],
55         'type': str,
56         'dest': 'mpdhost',
57         'default': None,
58         'help': 'MPD host, as IP or FQDN (default: localhost|MPD_HOST).'},
59     {
60         'sw': ['-P', '--port'],
61         'type': int,
62         'dest': 'mpdport',
63         'default': None,
64         'help': 'Port MPD in listening on (default: 6600|MPD_PORT).'},
65     {
66         'sw': ['--password'],
67         'type': str,
68         'dest': 'passwd',
69         'default': None,
70         'help': SUPPRESS},
71     {
72         'sw': ['--view_bl'],
73         'action': 'store_true',
74         'dest': 'view_bl',
75         'help': 'View black list.'},
76     {
77         'sw': ['--remove_bl'],
78         'type': int,
79         'help': 'Suppress a black list entry, by row id. Use --view_bl to get row id.'},
80     {
81         'sw': ['--bl_art'],
82         'type': str,
83         'metavar': 'ARTIST_NAME',
84         'help': 'Black list artist.'},
85     {
86         'sw': ['--bl_curr_art'],
87         'action': 'store_true',
88         'help': 'Black list currently playing artist.'},
89     {
90         'sw': ['--bl_curr_alb'],
91         'action': 'store_true',
92         'help': 'Black list currently playing album.'},
93     {
94         'sw': ['--bl_curr_trk'],
95         'action': 'store_true',
96         'help': 'Black list currently playing track.'},
97     {
98         'sw':['--purge_hist'],
99         'action': 'store_true',
100         'dest': 'do_purge_hist',
101         'help': 'Purge play history.'}])
102
103
104 class SimaDB_CLI(object):
105     """Command line management.
106     """
107
108     def __init__(self):
109         self.dbfile = self._get_default_dbfile()
110         self.parser = None
111         self.options = dict({})
112         self.localencoding = 'UTF-8'
113         self._get_encoding()
114         self.main()
115
116     def _get_encoding(self):
117         """Get local encoding"""
118         localencoding = getpreferredencoding()
119         if localencoding:
120             self.localencoding = localencoding
121
122     def _get_mpd_env_var(self):
123         """
124         MPD host/port environement variables are used if command line does not
125         provide host|port|passwd.
126         """
127         host, port, passwd = utils.get_mpd_environ()
128         if self.options.passwd is None and passwd:
129             self.options.passwd = passwd
130         if self.options.mpdhost is None:
131             if host:
132                 self.options.mpdhost = host
133             else:
134                 self.options.mpdhost = 'localhost'
135         if self.options.mpdport is None:
136             if port:
137                 self.options.mpdport = port
138             else:
139                 self.options.mpdport = 6600
140
141     def _declare_opts(self):
142         """
143         Declare options in ArgumentParser object.
144         """
145         self.parser = ArgumentParser(description=DESCRIPTION,
146                                    usage='%(prog)s [-h|--help] [options]',
147                                    prog='simadb_cli',
148                                    epilog='Happy Listening',
149                                    )
150
151         self.parser.add_argument('--version', action='version',
152                 version='%(prog)s {0}'.format(__version__))
153         # Add all options declare in OPTS
154         for opt in OPTS:
155             opt_names = opt.pop('sw')
156             self.parser.add_argument(*opt_names, **opt)
157
158     def _get_default_dbfile(self):
159         """
160         Use XDG directory standard if exists
161         else use "HOME/.local/share/mpd_sima/"
162         http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
163         """
164         homedir = expanduser('~')
165         dirname = 'mpd_sima'
166         if environ.get('XDG_DATA_HOME'):
167             data_dir = join(environ.get('XDG_DATA_HOME'), dirname)
168         else:
169             data_dir = join(homedir, '.local', 'share', dirname)
170         if not isdir(data_dir):
171             makedirs(data_dir)
172             chmod(data_dir, 0o700)
173         return join(data_dir, DB_NAME)
174
175     def _get_mpd_client(self):
176         """"""
177         host = self.options.mpdhost
178         port = self.options.mpdport
179         passwd = self.options.passwd
180         cli = MPDClient()
181         try:
182             cli.connect(host, port)
183             if passwd:
184                 cli.password(passwd)
185         except MPDError as err:
186             mess = 'ERROR: fail to connect MPD on %s:%s %s' % (
187                     host, port, err)
188             print(mess, file=stderr)
189             exit(1)
190         return cli
191
192     def _create_db(self):
193         """Create database if necessary"""
194         if isfile(self.dbfile):
195             return
196         print('Creating database!')
197         open(self.dbfile, 'a').close()
198         simadb.SimaDB(db_path=self.dbfile).create_db()
199
200     def _get_art_from_db(self, art):
201         """Return (id, name, self...) from DB or None is not in DB"""
202         db = simadb.SimaDB(db_path=self.dbfile)
203         art_db = db.get_artist(art, add_not=True)
204         if not art_db:
205             print('ERROR: "%s" not in data base!' % art, file=stderr)
206             return None
207         return art_db
208
209     def bl_artist(self):
210         """Black list artist"""
211         mpd_cli = self._get_mpd_client()
212         artists_list = mpd_cli.list('artist')
213         # Unicode cli given artist name
214         cli_artist_to_bl = self.options.bl_art
215         if cli_artist_to_bl not in artists_list:
216             print('Artist not found in MPD library.')
217             match = get_close_matches(cli_artist_to_bl, artists_list, 50, 0.78)
218             if match:
219                 print('You may be refering to %s' %
220                         '/'.join([m_a for m_a in match]))
221             return False
222         print('Black listing artist: %s' % cli_artist_to_bl)
223         db = simadb.SimaDB(db_path=self.dbfile)
224         db.get_bl_artist(cli_artist_to_bl)
225
226     def bl_current_artist(self):
227         """Black list current artist"""
228         mpd_cli = self._get_mpd_client()
229         artist = mpd_cli.currentsong().get('artist', '')
230         if not artist:
231             print('No artist found.')
232             return False
233         print('Black listing artist: %s' % artist)
234         db = simadb.SimaDB(db_path=self.dbfile)
235         db.get_bl_artist(artist)
236
237     def bl_current_album(self):
238         """Black list current artist"""
239         mpd_cli = self._get_mpd_client()
240         track = Track(**mpd_cli.currentsong())
241         if not track.album:
242             print('No album set for this track: %s' % track)
243             return False
244         print('Black listing album: {0}'.format(track.album))
245         db = simadb.SimaDB(db_path=self.dbfile)
246         db.get_bl_album(track)
247
248     def bl_current_track(self):
249         """Black list current artist"""
250         mpd_cli = self._get_mpd_client()
251         track = Track(**mpd_cli.currentsong())
252         print('Black listing track: %s' % track)
253         db = simadb.SimaDB(db_path=self.dbfile)
254         db.get_bl_track(track)
255
256     def purge_history(self):
257         """Purge all entries in history"""
258         db = simadb.SimaDB(db_path=self.dbfile)
259         print('Purging history...')
260         db.purge_history(duration=int(0))
261         print('done.')
262         print('Cleaning database...')
263         db.clean_database()
264         print('done.')
265
266     def view_bl(self):
267         """Print out black list."""
268         # TODO: enhance output formating
269         db = simadb.SimaDB(db_path=self.dbfile)
270         for bl_e in db.get_black_list():
271             print('\t# '.join([str(e) for e in bl_e]))
272
273     def remove_black_list_entry(self):
274         """"""
275         db = simadb.SimaDB(db_path=self.dbfile)
276         db._remove_bl(int(self.options.remove_bl))
277
278     def main(self):
279         """
280         Parse command line and run actions.
281         """
282         self._declare_opts()
283         self.options = self.parser.parse_args()
284         self._get_mpd_env_var()
285         if self.options.dbfile:
286             self.dbfile = self.options.dbfile
287             print('Using db file: %s' % self.dbfile)
288         if self.options.bl_art:
289             self.bl_artist()
290             return
291         if self.options.bl_curr_art:
292             self.bl_current_artist()
293             return
294         if self.options.bl_curr_alb:
295             self.bl_current_album()
296             return
297         if self.options.bl_curr_trk:
298             self.bl_current_track()
299             return
300         if self.options.view_bl:
301             self.view_bl()
302             return
303         if self.options.remove_bl:
304             self.remove_black_list_entry()
305             return
306         if self.options.do_purge_hist:
307             self.purge_history()
308         exit(0)
309
310
311 # Script starts here
312 if __name__ == '__main__':
313     try:
314         SimaDB_CLI()
315     except Exception as err:
316         print(err)
317         exit(1)
318
319 # VIM MODLINE
320 # vim: ai ts=4 sw=4 sts=4 expandtab