Browse code
temporary commit
fiddlerwoaroof authored on 23/05/2016 22:52:35
Showing 4 changed files
Showing 4 changed files
0 | 12 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,25 @@ |
1 |
+from __future__ import print_function |
|
2 |
+from twisted.internet.protocol import Protocol, ClientFactory |
|
3 |
+import sys |
|
4 |
+ |
|
5 |
+class WelcomeMessage(Protocol): |
|
6 |
+ def dataReceived(self, data): |
|
7 |
+ sys.stdout.write(data) |
|
8 |
+ def connectionMade(self): |
|
9 |
+ self.transport.write('Hello server, I am the client!\n') |
|
10 |
+ |
|
11 |
+class WelcomeMessageFactory(ClientFactory): |
|
12 |
+ def startedConnecting(self, connector): |
|
13 |
+ print('Started to connect.') |
|
14 |
+ def buildProtocol(self, addr): |
|
15 |
+ print('Connected.') |
|
16 |
+ return WelcomeMessage() |
|
17 |
+ def clientConnectionLost(self, connector, reason): |
|
18 |
+ print('Lost connection. Reason:', reason) |
|
19 |
+ def clientConnectionFailed(self, connector, reason): |
|
20 |
+ print('Connection failed. Reason:', reason) |
|
21 |
+ |
|
22 |
+ |
|
23 |
+from twisted.internet import reactor |
|
24 |
+d = reactor.connectTCP("localhost", 8007, WelcomeMessageFactory()) |
|
25 |
+reactor.run() |
... | ... |
@@ -12,32 +12,54 @@ class SelectableText(urwid.Text): |
12 | 12 |
def selectable(self): return True |
13 | 13 |
|
14 | 14 |
class Playlist(urwid.WidgetWrap): |
15 |
- def __init__(self, columns, plylst): |
|
16 |
- self.playlist = plylst |
|
15 |
+ def __init__(self, columns, col_display, plylst=None): |
|
16 |
+ if plylst == None: |
|
17 |
+ plylst = responsedict.ResponseDict() |
|
17 | 18 |
self.columns = columns |
18 |
- self.texts = self.get_texts(self.playlist.as_table(), cols) |
|
19 |
+ self.display_pref = col_display |
|
20 |
+ self.texts = self.get_texts(plylst.as_list()) |
|
21 |
+ self.body = urwid.SimpleFocusListWalker([]) |
|
22 |
+ self.make_playlist() |
|
19 | 23 |
|
20 |
- self.titles = urwid.Columns([('weight',w,SelectableText(c)) for w,c,_ in cols],1) |
|
24 |
+ self.titles = self.make_columns() |
|
21 | 25 |
self.header = urwid.Pile([self.titles, urwid.Divider('-')]) |
22 |
- widget = self.make_playlist() |
|
26 |
+ widget = urwid.ListBox(self.body) |
|
23 | 27 |
widget = urwid.Frame(widget, self.header) |
24 | 28 |
urwid.WidgetWrap.__init__(self, widget) |
25 | 29 |
|
26 |
- def get_texts(self, choices, columns): |
|
30 |
+ def make_columns(self): |
|
31 |
+ return urwid.Columns([ |
|
32 |
+ ('weight',self.display_pref[c].weight,SelectableText(self.display_pref[c].dispname)) |
|
33 |
+ for c in self.columns |
|
34 |
+ ],1) |
|
35 |
+ |
|
36 |
+ def get_texts(self, choices): |
|
37 |
+ #TODO: re-work column info to separate data from presentation info |
|
27 | 38 |
texts = [] |
28 |
- for weight,col,transform in columns: |
|
29 |
- texts.append([(weight,transform(x[0])) for x in choices.get(col)]) |
|
39 |
+ for x in choices: |
|
40 |
+ texts.append([]) |
|
41 |
+ for col in self.columns: |
|
42 |
+ display = self.display_pref[col] |
|
43 |
+ texts[-1].append((display.weight,display.transform(x.get(col,[""])[0]))) |
|
30 | 44 |
return texts |
31 | 45 |
|
32 |
- def make_playlist(self): |
|
33 |
- body = [] |
|
34 |
- for x in itertools.izip_longest(*self.texts): |
|
46 |
+ def update_texts(self, choices): |
|
47 |
+ self.texts = self.get_texts(choices.as_list()) |
|
48 |
+ self.update_playlist() |
|
49 |
+ |
|
50 |
+ def make_body(self): |
|
51 |
+ for x in self.texts: |
|
35 | 52 |
new_widg = [('weight',wgt,SelectableText(it,wrap='clip')) for wgt,it in x] |
36 | 53 |
new_widg = urwid.Columns(new_widg, 1) |
37 | 54 |
new_widg = urwid.AttrMap(new_widg, 'unselected', 'selected') |
38 |
- body.append(new_widg) |
|
39 |
- list_walker = urwid.SimpleFocusListWalker(body) |
|
40 |
- return urwid.ListBox(urwid.SimpleFocusListWalker(body[:])) |
|
55 |
+ self.body.append(new_widg) |
|
56 |
+ |
|
57 |
+ def update_playlist(self): |
|
58 |
+ del self.body[:] |
|
59 |
+ self.make_body() |
|
60 |
+ |
|
61 |
+ def make_playlist(self): |
|
62 |
+ self.make_body() |
|
41 | 63 |
|
42 | 64 |
def exit_on_q(key): |
43 | 65 |
if key in ('q', 'Q'): |
... | ... |
@@ -45,26 +67,73 @@ def exit_on_q(key): |
45 | 67 |
finally: reactor.stop() |
46 | 68 |
|
47 | 69 |
cols = [ |
48 |
- (1,'Pos', lambda x: str(int(x)+1)), |
|
49 |
- (4,'Album', lambda x:x), |
|
50 |
- (3,'Artist', lambda x:x), |
|
51 |
- (5,'Title', lambda x:x) |
|
70 |
+ 'Pos', |
|
71 |
+ 'Date', |
|
72 |
+ 'Album', |
|
73 |
+ 'Artist', |
|
74 |
+ 'AlbumArtist', |
|
75 |
+ 'Title', |
|
52 | 76 |
] |
53 | 77 |
|
78 |
+import collections |
|
79 |
+class DisplayDict(collections.Mapping): |
|
80 |
+ def __init__(self, data): |
|
81 |
+ self.row_class=collections.namedtuple('displaytuple', ['dispname','weight','transform']) |
|
82 |
+ self.data = self.normalize(data) |
|
83 |
+ def normalize(self, data): |
|
84 |
+ out = {} |
|
85 |
+ for key,val in data.iteritems(): |
|
86 |
+ dispname,weight,transform = None,1,lambda x:x |
|
87 |
+ if hasattr(val, '__iter__'): |
|
88 |
+ if len(val) == 3: dispname,weight,transform = val |
|
89 |
+ if len(val) == 2: dispname,weight = val |
|
90 |
+ if dispname is None: |
|
91 |
+ dispname = key |
|
92 |
+ out[key] = self.row_class(dispname, weight, transform) |
|
93 |
+ return out |
|
94 |
+ def __getitem__(self, key): |
|
95 |
+ head, _, tail = key.partition('.') |
|
96 |
+ if tail: |
|
97 |
+ try: |
|
98 |
+ return getattr(self.data[head], tail) |
|
99 |
+ except AttributeError, e: |
|
100 |
+ raise KeyError('Key not found: %s' % key) |
|
101 |
+ else: |
|
102 |
+ return self.data[head] |
|
103 |
+ def __iter__(self): |
|
104 |
+ return iter(self.data) |
|
105 |
+ def __len__(self): |
|
106 |
+ return len(self.data) |
|
107 |
+ |
|
108 |
+ |
|
109 |
+col_display = DisplayDict( |
|
110 |
+ dict( |
|
111 |
+ Pos=('#',1,lambda x: x and str(int(x) + 1)), |
|
112 |
+ Date=('Year', 4), |
|
113 |
+ Album=(None,8), |
|
114 |
+ Artist=(None,8), |
|
115 |
+ AlbumArtist=('Album Artist',5), |
|
116 |
+ Title=(None,13), |
|
117 |
+)) |
|
118 |
+ |
|
54 | 119 |
@defer.inlineCallbacks |
55 | 120 |
def get_playlist(): |
56 | 121 |
client = yield mpdprotocol.get_client() |
57 |
- res = yield client.sendCommand('playlistinfo') |
|
58 |
- method, data = res |
|
59 |
- loop.widget = Playlist(cols, data) |
|
122 |
+ method, data = yield client.sendCommand('playlistinfo') |
|
123 |
+ loop.widget.update_texts(data) |
|
60 | 124 |
loop.draw_screen() |
125 |
+ while True: |
|
126 |
+ idle = yield client.idle(['playlist']) |
|
127 |
+ method, data = yield client.sendCommand('playlistinfo') |
|
128 |
+ loop.widget.update_texts(data) |
|
129 |
+ loop.draw_screen() |
|
61 | 130 |
|
62 | 131 |
from twisted.internet import reactor |
63 | 132 |
palette = [ |
64 | 133 |
('selected', 'white', 'black'), |
65 | 134 |
('selected', 'black', 'white'), |
66 | 135 |
] |
67 |
-loop = urwid.MainLoop(urwid.SolidFill(), palette, event_loop=urwid.TwistedEventLoop(), unhandled_input=exit_on_q) |
|
136 |
+loop = urwid.MainLoop(Playlist(cols, col_display), palette, event_loop=urwid.TwistedEventLoop(), unhandled_input=exit_on_q) |
|
68 | 137 |
reactor.callWhenRunning(get_playlist) |
69 | 138 |
loop.run() |
70 | 139 |
|
71 | 140 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,65 @@ |
1 |
+# -*- coding: utf-8 -*- |
|
2 |
+from twisted.web import server, resource |
|
3 |
+from twisted.internet import reactor |
|
4 |
+from twisted.internet import task |
|
5 |
+ |
|
6 |
+import mpdprotocol |
|
7 |
+import jinja2 |
|
8 |
+import json |
|
9 |
+ |
|
10 |
+class MPDResource(resource.Resource): |
|
11 |
+ isLeaf = True |
|
12 |
+ jinja2env = jinja2.Environment(loader=jinja2.FileSystemLoader('templates')) |
|
13 |
+ |
|
14 |
+ def do(self, mpc, request): |
|
15 |
+ d = mpc.sendCommand('playlistinfo') |
|
16 |
+ |
|
17 |
+ cb = self.render_playlist |
|
18 |
+ if 'json' in request.args: |
|
19 |
+ if 'table' in request.args: |
|
20 |
+ cb = self.render_as_table_json |
|
21 |
+ else: |
|
22 |
+ cb = self.render_as_json |
|
23 |
+ d.addCallback(cb, request) |
|
24 |
+ return d |
|
25 |
+ |
|
26 |
+ def render_as_table_json(self, result, request): |
|
27 |
+ method, data = result |
|
28 |
+ request.setHeader('Content-Type', 'application/json') |
|
29 |
+ request.write(json.dumps(data.as_table())); |
|
30 |
+ request.finish() |
|
31 |
+ |
|
32 |
+ def render_as_json(self, result, request): |
|
33 |
+ method, data = result |
|
34 |
+ request.setHeader('Content-Type', 'application/json') |
|
35 |
+ request.write(json.dumps(data.as_list())); |
|
36 |
+ request.finish() |
|
37 |
+ |
|
38 |
+ def render_playlist(self, result, request): |
|
39 |
+ print('rendering playlist!') |
|
40 |
+ method, data = result |
|
41 |
+ template = self.jinja2env.get_template('playlist.html') |
|
42 |
+ tdata = [] |
|
43 |
+ for datum in data.as_list(): |
|
44 |
+ tdata.append([]) |
|
45 |
+ tdata[-1].append(('pos', datum['Pos'])) |
|
46 |
+ tdata[-1].append(('title', datum['Title'])) |
|
47 |
+ tdata[-1].append(('artist', datum['Artist'])) |
|
48 |
+ tdata[-1].append(('album', datum['Album'])) |
|
49 |
+ tdata[-1].append(('genre', datum['Genre'])) |
|
50 |
+ request.write(template.render(playlist=tdata).encode('utf-8')) |
|
51 |
+ request.finish() |
|
52 |
+ |
|
53 |
+ def failed(self, failure, request): |
|
54 |
+ print "whjat should I do here?" |
|
55 |
+ print self, failure, request |
|
56 |
+ failure.printTraceback() |
|
57 |
+ failure.raiseException() |
|
58 |
+ |
|
59 |
+ def render_GET(self, request): |
|
60 |
+ print('ello world!') |
|
61 |
+ d = mpdprotocol.get_client().addCallback(self.do, request) |
|
62 |
+ d.addErrback(self.failed, request) |
|
63 |
+ return server.NOT_DONE_YET |
|
64 |
+ |
|
65 |
+resource = MPDResource() |