communicator.py 33.1 KB
Newer Older
1
2
3
4
5
import os
import codecs
import urllib
import tempfile
import simplejson
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
6
import traceback
7
import datetime
8
9

from libraries.base.parsexml import parsexml
10
11
12
13
from modules.communication.liquidsoap.playerclient import LiquidSoapPlayerClient
from modules.communication.liquidsoap.recorderclient import LiquidSoapRecorderClient
from modules.communication.liquidsoap.initthread import LiquidSoapInitThread
from libraries.color.console import TerminalColors
14

15
16

class LiquidSoapCommunicator:
17
18
    lqc = None
    lqcr = None
19
    debug = False
20
    transaction = False
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
21
    controller = None
22
    channels = None
23
24

    # ------------------------------------------------------------------------------------------ #
25
    def __init__(self, config):
26
27

        """
28
29
30
31
32
33
34
35
36
        Constructor
        @type    lqs_socket: string
        @param   lqs_socket: Liquidsoap Player Socket
        @type    lqs_recsocket: string
        @param   lqs_recsocket: Liquidsoap Recorder Socket
        """
        self.debug = config.get("debug")
        self.lqc = LiquidSoapPlayerClient(config, "simplestmixer.sock")
        self.lqcr = LiquidSoapRecorderClient(config, "record.sock")
37

38
39
40
41
        self.__get_mixer_inputs__()

    def set_controller(self, controller):
        self.controller = controller
42

43
44
    # ------------------------------------------------------------------------------------------ #
    def switch_mixer_number(self, mixernumber, activate=True):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
45
        return self.__send_lqc_command__(self.lqc, "mixer", "select", mixernumber, activate)
46

47
48
49
    # ------------------------------------------------------------------------------------------ #
    def set_volume(self, mixernumber, volume):
        print("LiquidSoapCommunicator.set_volume")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
50
        return self.__send_lqc_command__(self.lqc, "mixer", "volume", mixernumber, volume)
51
52
53

    # ------------------------------------------------------------------------------------------ #
    def get_active_mixer(self):
54
55
56
57
        """
        get active mixer in liquidsoap server
        :return:
        """
58
59
        activeinputs = []

60
        # enable more control over the connection
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
61
62
        self.enable_transaction()
        self.open_conn(self.lqc)
63

64
        inputs = self.__get_mixer_inputs__()
65
66
67

        cnt = 0
        for input in inputs:
68
            status = self.__get_mixer_status__(cnt)
69
70
71
72
73
74

            if "selected=true" in status:
                activeinputs.append(input)

            cnt = cnt + 1

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
75
76
        self.close_conn(self.lqc)
        self.disable_transaction()
77
78
79

        return activeinputs

80
81
82
83
84
85
86
87
88
89
    # ------------------------------------------------------------------------------------------ #
    def get_active_channel(self):
        """
        gets active channel from programme
        :return:
        """
        active_source = self.controller.get_active_source()
        return self.channels.index(active_source)
        #return active_source

90
91
    # ------------------------------------------------------------------------------------------ #
    def get_mixer_status(self):
92
93
        inputstate = {}

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
94
95
        self.enable_transaction()
        self.open_conn(self.lqc)
96

97
        inputs = self.__get_mixer_inputs__()
98
99
100

        cnt = 0
        for input in inputs:
101
            inputstate[input] = self.__get_mixer_status__(cnt)
102
103
            cnt = cnt + 1

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
104
105
        self.disable_transaction()
        self.close_conn(self.lqc)
106
107
108

        return inputstate

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    # ------------------------------------------------------------------------------------------ #
    def activate_linein(self, entry):
        print(TerminalColors.OK_PINK + "LiquidSoapCommunicator is activating line in! Time: " + str(datetime.datetime.now()) + TerminalColors.ENDC)
        self.enable_transaction(True)
        self.channel_volume("fs", 0)
        self.channel_volume("http", 0)
        self.channel_volume("linein", entry.volume)
        self.disable_transaction(True)

    def activate_http(self, entry):
        print(TerminalColors.OK_PINK + "LiquidSoapCommunicator is activating http! Time: "+str(datetime.datetime.now()) + TerminalColors.ENDC)
        self.enable_transaction(True)
        self.channel_volume("fs", 0)
        self.channel_volume("http", entry.volume)
        self.channel_volume("linein", 0)
        self.disable_transaction(True)

    def activate_fs(self, entry):
        print(TerminalColors.OK_PINK + "LiquidSoapCommunicator is activating file system! Time: "+str(datetime.datetime.now()) + TerminalColors.ENDC)
        self.enable_transaction(True)
        self.channel_volume("fs", entry.volume)
        self.channel_volume("http", 0)
        self.channel_volume("linein", 0)
        self.disable_transaction(True)

134
135
    # ------------------------------------------------------------------------------------------ #
    def __get_mixer_inputs__(self):
136
137
138
139
        if self.channels is None:
            self.channels = self.__send_lqc_command__(self.lqc, "mixer", "inputs")

        return self.channels
140
141
142

    # ------------------------------------------------------------------------------------------ #
    def __get_mixer_status__(self, mixernumber):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
143
        return self.__send_lqc_command__(self.lqc, "mixer", "status", mixernumber)
144
145
146
147

    # ------------------------------------------------------------------------------------------ #
    def liquid_startup(self):
        LiquidSoapInitThread(self, self.lqc).start()
148
149


150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
        return ""
        #self.openConn(self.lqc)
        #reply = self.lqc.init()
        #self.closeConn(self.lqc)
        #return reply

    # ------------------------------------------------------------------------------------------ #
    def get_client(self):
        return self.lqc

    # ------------------------------------------------------------------------------------------ #
    def all_data(self):
        """
        Gibt Metadaten aller Kanäle als JSON-String an den Client zurück
        @rtype:   string/None
        @return:  Die Antwort des Liquidsoap-Servers

        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
168
        channels = self.__send_lqc_command__(self.lqc, 'listChannels')
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

        if not isinstance(channels, list):
            self.warning('01')
            self.notifyClient()
            return

        data = {}
        pdata = {}

        try:
            self.is_intern = True
            playlist_data = simplejson.loads(self.playlist_data(True))
            self.is_intern = False
        except:
            self.warning('01')
            self.notifyClient()
            return

        # Status des Playlistkanals abfragen
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
188
        status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', '0')
189
190
191
192
193
194
195
196
197
198
199
200
201
202

        states = status.split(' ')
        state_data = {}

        # Die Stati in python dicts einlesen
        for state in states:
            item = state.split('=')
            try:
                state_data[item[0]] = item[1]
            except:
                self.warning('01')
                self.notifyClient()
                return

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
203
        remaining = self.__send_lqc_command__(self.lqc, 'playlist_remaining')
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
        state_data['remaining'] = remaining
        # Die Metadaten der Playlist
        pdata['state'] = state_data
        pdata['tracks'] = playlist_data
        data['playlist'] = pdata

        # Servermeldungen abschalten
        self.is_intern = True
        # die channel queues einlesen

        for channel in channels:
            data[channel] = self.channel_queue(channel, True)
        # Servermeldungen einschalten
        self.is_intern = False
        self.success('00', data)
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def ping(self):
        """
        dem Client antworten
        """
        return self.message('OK')

    # ------------------------------------------------------------------------------------------ #
    def channel_insert(self, channel, uri, pos):
        """
        Track in einen Channel einfuegen
        @type     channel: string
        @param    channel: Kanal
        @type     uri:     string
        @param    uri:     Uri - z.B. file:///my/audio/mp3
        @type     pos:     int
        @param    pos:     Die Position an der eingefügt werden soll
        @rtype:   string/None
        @return:  Die Antwort des Liquidsoap-Servers
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
241
        message = self.__send_lqc_command__(self.lqc, 'insert', uri, pos, channel)
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
        message = message.strip()

        try:
            if int(message) > -1:
                self.success()
                return self.message(message)
        except:
            self.warning('01')
            self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def channel_move(self, channel, fromPos, toPos):
        """
        Channel-Eintrag von Position fromPos nach Position toPos verschieben
        @type     channel: string
        @param    channel: Kanal
        @type     fromPos: int
        @param    fromPos: die Position des Eintrags, der verschoben wird
        @type     toPos:   int
        @param    toPos:   Zielposition
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
265
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel, 'secondary_queue')
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286

        rids = message.strip().split(' ')

        try:
            rid = rids[int(fromPos) - 1]
        except:
            self.warning('01')
            self.notifyClient()
            return
        try:
            target = rids[int(toPos) - 1]
        except:
            self.warning('01')
            self.notifyClient()
            return

        if rids[int(fromPos) - 1] == rids[int(toPos) - 1]:
            self.warning('02')
            self.notifyClient()
            return

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
287
        message = self.__send_lqc_command__(self.lqc, 'move', rid, str(int(toPos) - 1), channel)
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
        message = message.strip()

        if message.strip().find('OK') > -1:
            self.success()
            self.notifyClient()
            return
        else:
            self.warning('03')
            self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def channel_off(self, channel):
        """
        Channel deaktivieren
        @type     channel: string
        @param    channel: Kanal
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers
        """
        # internal channel name for playlist is 'common'
        if channel == 'playlist':
            channel = 'common'

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
311
        channels = self.__send_lqc_command__(self.lqc, 'listChannels', False)
312
313

        index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
314
        message = self.__send_lqc_command__(self.lqc, 'deactivate', str(index))
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
        if message.find('selected=false'):
            self.success()
        else:
            self.warning('01')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def channel_on(self, channel):
        """
        Channel aktivieren
        @type     channel: string
        @param    channel: Kanal
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers
        """

        # Find channels
        if channel == 'playlist':
            channel = 'common'

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
336
        channels = self.__send_lqc_command__(self.lqc, 'listChannels', False)
337
338
339

        index = channels.index(channel)
        # a activate channel
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
340
        message = self.__send_lqc_command__(self.lqc, 'activate', str(index))
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363

        if message.find('selected=true'):
            self.success()
        else:
            self.warning('01')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def channel_queue(self, channel, raw=False):
        """
        Channel Queue abrufen
        @type     channel: string
        @param    channel: Kanal
        @type     raw:     boolean
        @param    raw:     Wenn true, Rückgabe als Python dict Object, andernfalls als JSON-String
        @rtype:   string/dict
        @return:  Der Channel Queue
        """
        data = {}

        # queue will return request id's (rids)

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
364
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel)
365
366
367
368
369
370

        rids = message.strip().split(' ')
        data['tracks'] = []
        for rid in rids:
            if rid != '':
                # get each rids metadata
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
371
                metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
372
373
374
375
376
377
378
379
380
381
                track = self._metadata_format(metadata)
                if not 'title' in track:
                    if 'location' in track:
                        track['title'] = os.path.basename(track['location'])
                    elif 'filename' in track:
                        track['title'] = os.path.basename(track['filename'])
                    else:
                        track['title'] = 'unknown'

                data['tracks'].extend([track])
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
382
        channels = self.__send_lqc_command__(self.lqc, 'listChannels')
383
384
385
386
387
388
389

        """
        now get channels state 
        self.lqc.status: ready=false volume=100% single=false selected=false remaining=0.00
        """
        try:
            index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
390
            status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(index + 1))
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
            states = status.split(' ')
            state_data = {}
            for state in states:
                item = state.split('=')
                if len(item) > 1:
                    state_data[item[0]] = item[1]
        except:
            state_data = {}
            self.error('01')
            self.notifyClient()
            return

        data['state'] = state_data

        if raw:
            # return the list internal
            data['state'] = state_data
            return data
        else:
            self.success('00', data)
            self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def channel_remove(self, channel, pos):
        """
        Channel-Eintrag löschen
        @type     channel: string
        @param    channel: Kanal
        @type     pos:     int
        @param    pos:     Position des Eintrags
        """
        # Es kann nur vom Secondary Queue gelöscht werden
        # Falls der Track im Primary Queue gelöscht werden soll, ist ein skip nötg

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
425
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel, 'secondary_queue')
426
427
428
429
430
431
432
        rids = message.strip().split(' ')
        try:
            rid = rids[int(pos) - 1]
        except:
            self.warning('02')
            self.notifyClient()
            return
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
433
        message = self.__send_lqc_command__(self.lqc, 'remove', rid, channel)
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
        if message.find('OK') > -1:
            self.success()
        else:
            self.warning('01')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def channel_seek(self, channel, duration):
        """
        Im aktuell spielenden Track auf dem Kanal <channel> <duration> Sekunden "vorspulen"
        @type     channel:  string
        @param    channel:  Kanal
        @type     duration: int
        @param    duration: Dauer in Sekunden
        """
        # Liquidsoap Kommando
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
451
        data = self.__send_lqc_command__(self.lqc, 'seek', duration, channel)
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469

        # Resultate prüfen
        if self._check_result(data):
            self.success('00', self.lq_error['value'])
        else:
            self.warning('01')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def channel_skip(self, channel):
        """
        Kanal skippen
        @type     channel:  string
        @param    channel:  Kanal
        """

        # Liquidsoap Kommando
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
470
        channels = self.__send_lqc_command__(self.lqc, 'listChannels')
471
472
473
474
475
476
477

        foundChannel = ''
        if not isinstance(channels, list):
            self.error('02')
        else:
            for index, item in enumerate(channels):
                if item == channel:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
478
                    foundChannel = self.__send_lqc_command__(self.lqc, 'skip', 'mixer', str(index + 1))
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
                    break

            if foundChannel.strip().find('OK') > -1:
                self.success()
            elif len(channels) < 1:
                self.warning('01')
            else:
                self.error('03')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def channel_volume(self, channel, volume):
        """
        Lautstärke auf Kanal <channel> setzen
        @type     channel:  string
        @param    channel:  Kanal
        @type     volume:   int
        @param    volume:  Lautstärke von 1-100
        """

        if channel == 'playlist':
            channel = 'common'
        # Liquidsoap Kommando
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
503
        #channels = self.__sendLqcCommand__(self.lqc, 'listChannels', False)
504
505
        channels = self.__get_mixer_inputs__()

506
507
508
509
510
511
512
513
        try:
            index = channels.index(channel)
            if len(channel) < 1:
                self.warning('02')
        except:
            self.error('03')

        else:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
514
            message = self.__send_lqc_command__(self.lqc, 'mixer', 'volume', str(index), str(int(volume)))
515
516

            if message.find('volume=' + str(volume) + '%'):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
517
                self.controller.success('01', str(volume))
518
            else:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
519
                self.controller.warning('01')
520
521
522
523
524
525
526
527
528

    # ------------------------------------------------------------------------------------------ #
    def current_data(self):
        """
        Metadaten des gespielten Tracks im JSON-Format
        Beispiel: {"title": "Deserted Cities of the Heart", "filename": "/home/michel/Nas-audio/cream/the_very_best_of/17_Deserted_Cities_of_the_Heart.mp3", "source": "ch2", "on_air": "2014/07/23 23:46:37",  "rid": "2"}
        """

        # Liquidsoap Kommando
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
529
        message = self.__send_lqc_command__(self.lqc, 'currentTrack')
530
531
532

        rid = message.strip()

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
533
        metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550

        data = self._metadata_format(metadata)

        if data:
            self.success('00', simplejson.dumps(data))
        elif rid == '':
            self.warning('01')
        else:
            self.warning('02', rid)

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def get_channel_state(self, channel):

        if channel == 'playlist':
            channel = 'common'
551

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
552
        channels = self.__send_lqc_command__(self.lqc, 'listChannels', False)
553
554
555
556
557

        index = channels.index(channel)
        state_data = {}
        try:
            index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
558
            status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(index + 1))
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
            states = status.split(' ')
            for state in states:
                item = state.split('=')
                if len(item) > 1:
                    state_data[item[0]] = item[1]
        except:
            state_data = {}
            self.error('01')
            self.notifyClient()
            return
        self.success('00', simplejson.dumps(state_data))
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def help(self):
        """
        Gibt die Hilfe aus
        """

        errNum = '11'
        try:
            file = open(os.path.dirname(os.path.abspath(__file__)) + '/doc/comba.hlp', 'r')
            doc = file.read()
            return self.message(doc)
        except:
            self.warning('01')
            self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def liquidsoap_help(self):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
589
        data = self.__send_lqc_command__(self.lqc, 'help')
590
591
592
593
594
595
596
597
598
599
600
601
602
        if not data:
            self.warning('01')
        else:
            self.success('00', data)
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def list_channels(self):
        """
        Channels auflisten (Simple JSON)
        """
        # Liquidsoap Kommando

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
603
        channels = self.__send_lqc_command__(self.lqc, 'listChannels')
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620

        if not isinstance(channels, list):
            self.error('02')
        elif len(channels) < 1:
            self.warning('01')
        else:
            self.success('00', channels)

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_data(self, raw=False):
        """
        Aktuelle Playlist Daten im JSON-Format
        """

        # Liquidsoap Kommando
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
621
        data = self.__send_lqc_command__(self.lqc, 'playlistData')
622
623
624
625
626
627
628
629
630
631
632
        if not raw:
            self.success('00', simplejson.loads(data))
            self.notifyClient()
        else:
            return data

    # ------------------------------------------------------------------------------------------ #
    def playlist_flush(self):
        """
        Aktuelle Playlist leeren
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
633
        data = self.__send_lqc_command__(self.lqc, 'flush')
634
635
636
637
638
639
640
641
        self.success('00')
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_insert(self, uri, pos):
        """
        Track in die Playlist einfuegen
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
642
        data = self.__send_lqc_command__(self.lqc, 'insert', uri, pos)
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
        if not self._check_result(data):
            self.warning('01')
        else:
            self.success('00')
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_load(self, uri):
        """
        Playlist laden
        @type   uri:   string
        @param  uri:   Uri der Playlist
        """
        try:
            xml = urllib.urlopen(uri).read().decode('utf8')

        except:
            try:
                xml = open(uri).read().decode('utf8')
            except:
                self.error('01', self.lq_error['message'])
                self.notifyClient()
                return

        (num, filename) = tempfile.mkstemp(suffix=".xspf")

        with codecs.open(filename, "w", encoding='utf8') as text_file:
            text_file.write(xml)

        playlist = parsexml(xml)

        if not isinstance(playlist, dict):
            self.error('02')
            self.notifyClient()
        else:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
678
679
            self.__send_lqc_command__(self.lqc, 'flush')
            data = self.__send_lqc_command__(self.lqc, 'loadPlaylist', filename)
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701

            if not self._check_result(data):
                self.error('01', self.lq_error['message'])

            else:
                os.remove(filename)
                self._updateEventQueue(playlist)
                event = {'job': 'loadplaylist', 'uri': uri}
                self.messenger.fire_event('loadplaylist', event, 'player')
                self.success('00')

            self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_move(self, fromPos, toPos):
        """
        Playlist-Eintrag von Position fromPos nach Position toPos verschieben
        @type     fromPos: int
        @param    fromPos: die Position des Eintrags, der verschoben wird
        @type     toPos:   int
        @param    toPos:   Zielposition
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
702
        data = self.__send_lqc_command__(self.lqc, 'move', str(int(fromPos) + 1), str(int(toPos) + 1))
703
704
705
706
707
708
709
710
711
712
713
714
715

        if not self._check_result(data):
            self.warning('01')
        else:
            self.success('00')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_pause(self):
        """
        Playlist pausieren
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
716
        data = self.__send_lqc_command__(self.lqc, 'pause')
717
718
719
720
721
722
723
724
725
726
727
728
729
730

        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_stop(self):
        """
        Playlist stoppen - der Kanal wird deaktiviert
        """
        # Kanal 0 (Playlist) deaktivieren
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
731
        self.__send_lqc_command__(self.lqc, 'deactivate', '0')
732

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
733
        data = self.__send_lqc_command__(self.lqc, 'pause')
734
735
736
737
738
739
740

        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00')

        self.notifyClient()
741
742

    # ------------------------------------------------------------------------------------------ #
743
744
745
746
747
748
749
    def playlist_play(self, when='now'):
        """
        Playlist starten
        @type   when:   string
        @param  when:   Wenn "now" werden alle anderen Kanäle deaktiviert und geskipped
        """
        # Playlist Kanal aktivieren
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
750
        self.__send_lqc_command__(self.lqc, 'activate', '0')
751
752
753
754

        if when == 'now':
            # immediately skip all playing channels
            # and activate the playlist channel
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
755
            channels = self.__send_lqc_command__(self.lqc, 'listChannels')
756
757
758
759
760
761
762
            if not isinstance(channels, list):
                self.error('03')
            elif len(channels) < 1:
                self.warning('02')
            else:
                # xrange
                for i in range(len(channels)):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
763
                    status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(i + 1))
764
                    if "selected=true" in status:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
765
766
767
                        status = self.__send_lqc_command__(self.lqc, 'deactivate', str(i + 1))
                        status = self.__send_lqc_command__(self.lqc, 'skip', 'mixer', str(i + 1))
                        self.__send_lqc_command__(self.lqc, 'activate', '0')
768
769

        # send the play command
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
770
        data = self.__send_lqc_command__(self.lqc, 'play')
771
772
773
774
775
776
777
778
779
780
781
782
783
784
        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_push(self, uri):
        """
        Eine Uri in die Playlist einfügen
        @type   uri:   str
        @param  uri:   Die Uri
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
785
        data = self.__send_lqc_command__(self.lqc, 'push', uri)
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800

        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_remove(self, pos):
        """
        Playlist-Eintrag löschen
        @type     pos:     int
        @param    pos:     Position des Eintrags
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
801
        data = self.__send_lqc_command__(self.lqc, 'remove', pos)
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816

        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_seek(self, duration):
        """
        Im aktuell spielenden Track auf dem der Playlist "vorspulen"
        @type     duration: int
        @param    duration: Dauer in Sekunden
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
817
        data = self.__send_lqc_command__(self.lqc, 'seek', duration)
818
819
820
821
822
823
824
825
826
827
828
829
830
831

        # Resultate prüfen
        if self._check_result(data):
            self.success('00', self.lq_error['value'])
        else:
            self.warning('01')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_skip(self):
        """
        Playlist skippen
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
832
        data = self.__send_lqc_command__(self.lqc, 'skip')
833
834
835
836
837
838
839
840
841
842

        self.success('00')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def version(self):
        """
        get version
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
843
        data = self.__send_lqc_command__(self.lqc, 'version')
844
845
        self.success('00', data)
        self.notifyClient()
846
847

    # ------------------------------------------------------------------------------------------ #
848
849
850
851
    def uptime(self):
        """
        get uptime
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
852
        data = self.__send_lqc_command__(self.lqc, 'uptime')
853
854
855
856
857
858
859
860
        self.success('00', data)
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def on_air(self):
        """
        get whats playing now
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
861
        data = self.__send_lqc_command__(self.lqc, 'on_air')
862
863
864
865
866
867
868
869
870
871
        self.success('00', data)
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def recorder_start(self):
        """
        Recorder starten
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
872
        message = self.__send_lqc_command__(self.lqcr, 'start_record')
873
874
875
876
877
878
879
880
881
882
883
884
        if message.strip() == 'OK':
            self.success('00')
        else:
            self.warning('01')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def recorder_stop(self):
        """
        Recorder stoppen
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
885
        message = self.__send_lqc_command__(self.lqcr, 'stop_record')
886
887
888
889
890
891
892
893
894
895
896
897
898
899

        if message.strip() == 'OK':
            self.success('00')
        else:
            self.warning('01')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def recorder_data(self):
        """
        Status-Daten des Recorders
        Rückgabe-Beispiel: /var/audio/rec/2014-05-13/2014-05-13-22-00.wav,30 - Aufnahme von 30% der angegebenen Audiodatei
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
900
        message = self.__send_lqc_command__(self.lqcr, 'recorder_data')
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
        l = message.split(',')
        data = {}

        if not isinstance(l, list):
            data = {'file': '', 'recorded': ''}
            self.warning('01')
        else:
            data['file'] = l[0]
            if len(l) > 1:
                data['recorded'] = l[1]
            else:
                data['recorded'] = ''
            self.success('00', data)

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
918
    def __send_lqc_command__(self, lqs_instance, namespace, command, *args):
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
        """
        Ein Kommando an Liquidsoap senden
        @type  lqs_instance: object
        @param lqs_instance: Instance of LiquidSoap Client
        @type  namespace:    string
        @param namespace:    Namespace of function
        @type  command:      string
        @param command:      Function name
        @type args:          list
        @param args:         List of parameters
        @rtype:              string
        @return:             Response from LiquidSoap
        """
        try:
            # connect
934
            if not self.transaction:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
935
                self.open_conn(lqs_instance)
936

937
            if self.debug:
938
                print("LiquidSoapCommunicator is calling " + str(namespace) + "." + str(command) + str(args))
939

940
941
942
943
944
            # call wanted function ...
            func = getattr(lqs_instance, namespace)
            # ... and fetch the result
            result = func(command, *args)

945
946
947
948
949
950
            if self.debug:
                if len(args) == 0:
                    print("LiquidSoapCommunicator got response " + str(result))
                else:
                    print("LiquidSoapCommunicator got response " + str(result))

951
            # say byebye
952
            if not self.transaction:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
953
                self.close_conn(lqs_instance)
954
955
956

        except Exception as e:
            # Verbindung gescheitert - Fehler an Client
957
958
959
960
961
962
            #            if command.find('record') > -1:
            #                raise e
            # self.fatal('02')
            #            else:
            #                raise e
            # self.fatal('01')
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
963
            traceback.print_exc()
964
965
            raise e

966
            # Instanz/Thread zerstören - aufrufende Funktion wird nicht weiter abgearbeitet
967
            #del self
968
969
970
        else:
            return result

971
    # ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
972
    def enable_transaction(self, connect=False):
973
        self.transaction = True
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
974
        if connect:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
975
            self.open_conn()
976

977
    # ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
978
    def disable_transaction(self, disconnect=False):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
979
        if disconnect:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
980
            self.close_conn()
981
982
        self.transaction = False

983
    # ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
984
985
    def open_conn(self, socket=None):
        if socket is None:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
986
            socket = self.lqc
987
        if self.debug:
988
            print(TerminalColors.OK_GREEN + "LiquidSoapCommunicator opened conn" + TerminalColors.ENDC)
989
990
        socket.connect()

991
    # ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
992
    def close_conn(self, socket=None):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
993
994
        if socket == None:
            socket = self.lqc
995

996
        socket.byebye()
997
998
999

        if self.debug:
            print(TerminalColors.OK_BLUE + "LiquidSoapCommunicator closed conn" + TerminalColors.ENDC)