Revisão | 2ac863660e598047476a9b69ffbc7d05be4fd240 (tree) |
---|---|
Hora | 2019-11-02 07:12:31 |
Autor | Jaime Marquínez Ferrándiz <jaime.marquinez.ferrandiz@fast...> |
Commiter | Jaime Marquínez Ferrándiz |
Initial commit
@@ -0,0 +1,24 @@ | ||
1 | +This is free and unencumbered software released into the public domain. | |
2 | + | |
3 | +Anyone is free to copy, modify, publish, use, compile, sell, or | |
4 | +distribute this software, either in source code form or as a compiled | |
5 | +binary, for any purpose, commercial or non-commercial, and by any | |
6 | +means. | |
7 | + | |
8 | +In jurisdictions that recognize copyright laws, the author or authors | |
9 | +of this software dedicate any and all copyright interest in the | |
10 | +software to the public domain. We make this dedication for the benefit | |
11 | +of the public at large and to the detriment of our heirs and | |
12 | +successors. We intend this dedication to be an overt act of | |
13 | +relinquishment in perpetuity of all present and future rights to this | |
14 | +software under copyright law. | |
15 | + | |
16 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
17 | +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
18 | +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
19 | +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
20 | +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
21 | +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
22 | +OTHER DEALINGS IN THE SOFTWARE. | |
23 | + | |
24 | +For more information, please refer to <http://unlicense.org/> |
@@ -0,0 +1,2 @@ | ||
1 | +from pkgutil import extend_path | |
2 | +__path__ = extend_path(__path__, __name__) |
@@ -0,0 +1,86 @@ | ||
1 | +import os.path | |
2 | +import sqlite3 | |
3 | +import sys | |
4 | + | |
5 | +from beets.library import Library | |
6 | +from beets.plugins import BeetsPlugin | |
7 | +from beets.ui import Subcommand | |
8 | + | |
9 | +IS_PY3 = sys.version_info[0] >= 3 | |
10 | + | |
11 | + | |
12 | +class MpdRatings(BeetsPlugin): | |
13 | + beets_rating_attr = 'stars' | |
14 | + beets_rating_range = 5 | |
15 | + mpd_rating_sticker = 'rating' | |
16 | + mpd_rating_range = 10 | |
17 | + | |
18 | + def commands(self): | |
19 | + ratings_command = Subcommand('mpd-ratings', help='Sync ratings with mpd stickers') | |
20 | + ratings_command.func = self.mpd_ratings | |
21 | + ratings_command.parser.add_option( | |
22 | + u'-p', u'--pretend', action='store_true', | |
23 | + help=u'show all changes but do nothing') | |
24 | + return [ratings_command] | |
25 | + | |
26 | + def mpd_ratings(self, lib: Library, opts, args): | |
27 | + sticker_db = self.config['stickerdb'].get() | |
28 | + pretend = opts.pretend | |
29 | + self.export_to_mpd(lib, os.path.expanduser(sticker_db), pretend) | |
30 | + | |
31 | + def export_to_mpd(self, library: Library, sticker_db: str, pretend: bool): | |
32 | + self._log.info('Exporting ratings to mpd stickers') | |
33 | + lib_directory = library.directory | |
34 | + if IS_PY3 and isinstance(lib_directory, bytes): | |
35 | + lib_directory = lib_directory.decode() | |
36 | + | |
37 | + self._log.info('Comparing ratings between mpd and beets') | |
38 | + conn = sqlite3.connect(library.path) | |
39 | + try: | |
40 | + conn.execute('ATTACH ? AS stickers', (sticker_db,)) | |
41 | + | |
42 | + to_update = conn.execute(''' | |
43 | + SELECT s.value, attr.value * ?1/?2 AS valueAttr, REPLACE(i.path, ?3, '') FROM items i | |
44 | + JOIN item_attributes attr ON i.id=attr.entity_id | |
45 | + LEFT JOIN stickers.sticker s on (?3 || s.uri) LIKE i.path | |
46 | + where (s.name = 'rating' OR s.name is NULL) | |
47 | + AND (s.type = 'song' OR s.type is NULL) | |
48 | + AND attr.key = 'stars' | |
49 | + AND (s.value IS NULL OR s.value != valueAttr) | |
50 | + ''', ( | |
51 | + self.mpd_rating_range, | |
52 | + self.beets_rating_range, | |
53 | + lib_directory + '/' | |
54 | + )).fetchall() | |
55 | + finally: | |
56 | + conn.close() | |
57 | + | |
58 | + if pretend: | |
59 | + for mpd_rating, beet_rating, path in to_update: | |
60 | + if mpd_rating is None: | |
61 | + self._log.info('Adding rating {} to {}', beet_rating, path) | |
62 | + else: | |
63 | + self._log.info('Updating rating from {} to {} to {}', mpd_rating, beet_rating, path) | |
64 | + else: | |
65 | + conn_sticker = sqlite3.connect(sticker_db) | |
66 | + try: | |
67 | + self._log.info('Adding missing ratings') | |
68 | + with conn_sticker: | |
69 | + conn_sticker.executemany(''' | |
70 | + INSERT INTO sticker (type, uri, name, value) VALUES ('song', ?, ?, ?) | |
71 | + ''', ( | |
72 | + (x[2], self.mpd_rating_sticker, x[1]) | |
73 | + for x in to_update if x[0] is None | |
74 | + )) | |
75 | + | |
76 | + self._log.info('Updating outdated ratings') | |
77 | + with conn_sticker: | |
78 | + conn_sticker.executemany(''' | |
79 | + UPDATE sticker SET value=? | |
80 | + WHERE uri=? AND name=? | |
81 | + ''', ( | |
82 | + (x[1], x[2], self.mpd_rating_sticker) | |
83 | + for x in to_update if x[0] is not None | |
84 | + )) | |
85 | + finally: | |
86 | + conn_sticker.close() |