communicator.py 34.8 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
from modules.communication.liquidsoap.playerclient import LiquidSoapPlayerClient
from modules.communication.liquidsoap.recorderclient import LiquidSoapRecorderClient
from modules.communication.liquidsoap.initthread import LiquidSoapInitThread
13
14
from libraries.enum.consolecolor import TerminalColors
from libraries.exceptions.auraexceptions import LQConnectionError
15
from libraries.database.broadcasts import TrackService
16
from libraries.exceptions.exception_logger import ExceptionLogger
17

18

19
class LiquidSoapCommunicator(ExceptionLogger):
20
21
    lqc = None
    lqcr = None
22
    debug = False
23
24
    transaction = 0
    # controller = None
25
    channels = None
26
    scheduler = None
27
28

    # ------------------------------------------------------------------------------------------ #
29
    def __init__(self, config):
30
        """
31
32
33
34
35
36
37
38
39
        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")
40

41
42
43
44
        #try:
        #    self.get_all_channels()
        #except LQConnectionError as lqe:
        #    print(lqe)
45

46
47
    #def set_controller(self, controller):
    #    self.controller = controller
48

49

50

51
52
53
    # ------------------------------------------------------------------------------------------ #
    def set_volume(self, mixernumber, volume):
        print("LiquidSoapCommunicator.set_volume")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
54
        return self.__send_lqc_command__(self.lqc, "mixer", "volume", mixernumber, volume)
55
56
57

    # ------------------------------------------------------------------------------------------ #
    def get_active_mixer(self):
58
59
60
61
        """
        get active mixer in liquidsoap server
        :return:
        """
62
63
        activeinputs = []

64
        # enable more control over the connection
65
        self.enable_transaction(True)
66

67
        inputs = self.get_all_channels()
68
69
70

        cnt = 0
        for input in inputs:
71
            status = self.__get_mixer_status__(cnt)
72
73
74
75
76
77

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

            cnt = cnt + 1

78
        self.disable_transaction(True)
79
80
81

        return activeinputs

82
83
84
85
86
87
    # ------------------------------------------------------------------------------------------ #
    def get_active_channel(self):
        """
        gets active channel from programme
        :return:
        """
88
        active_source = self.scheduler.get_active_source()
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
89
90
        if active_source == "":
            return ""
91
92
        return self.channels.index(active_source)

93
94
    # ------------------------------------------------------------------------------------------ #
    def get_mixer_status(self):
95
96
        inputstate = {}

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
97
98
        self.enable_transaction()
        self.open_conn(self.lqc)
99

100
        inputs = self.get_all_channels()
101
102
103

        cnt = 0
        for input in inputs:
104
            inputstate[input] = self.__get_mixer_status__(cnt)
105
106
            cnt = cnt + 1

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
107
108
        self.disable_transaction()
        self.close_conn(self.lqc)
109
110
111

        return inputstate

112
    # ------------------------------------------------------------------------------------------ #
113
114
    def activate(self, entry, input_type):
        if self.scheduler.get_active_source() == input_type:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
115
            self.insert_track_service_entry(entry)
116
117
118
119
120
121
122
123
124
125
            print(TerminalColors.OK_PINK.value + "LiquidSoapCommunicator " + input_type + " already active! Time: " + str(datetime.datetime.now()) + TerminalColors.ENDC.value)
        else:
            print(TerminalColors.OK_PINK.value + "LiquidSoapCommunicator is activating " + input_type + "! Time: " + str(datetime.datetime.now()) + TerminalColors.ENDC.value)

            self.enable_transaction(True)
            others = self.all_inputs_but(input_type)
            for o in others:
                self.channel_volume(o, 0)
            self.channel_volume(input_type, entry.volume)
            self.disable_transaction(True)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
126
            self.insert_track_service_entry(entry)
127
            self.playlist_insert()
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
128
129
130

    # ------------------------------------------------------------------------------------------ #
    def insert_track_service_entry(self, schedule_entry):
131
132
133
134
        trackservice_entry = TrackService()
        trackservice_entry.playlist_id = schedule_entry.playlist_id
        trackservice_entry.entry_num = schedule_entry.entry_num
        trackservice_entry.source = schedule_entry.source
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
135

136
        trackservice_entry.store(add=True, commit=True)
137
138
139

    # ------------------------------------------------------------------------------------------ #
    def all_inputs_but(self, input_type):
140
141
142
143
144
145
146
147
        activemixer_copy = self.get_all_channels().copy() # get_active_mixer().copy()
        print("holding ", activemixer_copy)
        try:
            activemixer_copy.remove(input_type)
        except ValueError:
            # Value not in list
            return activemixer_copy

148
149
150
151
        return activemixer_copy

    # ------------------------------------------------------------------------------------------ #
    def get_all_channels(self):
152
        if self.channels is None or self.channels == ['']:
153
154
155
            self.channels = self.__send_lqc_command__(self.lqc, "mixer", "inputs")

        return self.channels
156

157
158
159
160
161
    # ------------------------------------------------------------------------------------------ #
    def reload_channels(self):
        self.channels = None
        return self.get_all_channels()

162
163
    # ------------------------------------------------------------------------------------------ #
    def __get_mixer_status__(self, mixernumber):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
164
        return self.__send_lqc_command__(self.lqc, "mixer", "status", mixernumber)
165
166

    # ------------------------------------------------------------------------------------------ #
167
    def init_player(self):
168
        LiquidSoapInitThread(self, self.lqc).start()
169
        return "Started LiquidSoapInitThread!"
170
171
172
173
174
175
176
177
178
179
180
181
182

    # ------------------------------------------------------------------------------------------ #
    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
183
        channels = self.__send_lqc_command__(self.lqc, 'listChannels')
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

        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
203
        status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', '0')
204
205
206
207
208
209
210
211
212
213
214
215
216
217

        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
218
        remaining = self.__send_lqc_command__(self.lqc, 'playlist_remaining')
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
        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
256
        message = self.__send_lqc_command__(self.lqc, 'insert', uri, pos, channel)
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
        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
280
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel, 'secondary_queue')
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

        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
302
        message = self.__send_lqc_command__(self.lqc, 'move', rid, str(int(toPos) - 1), channel)
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
        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
326
        channels = self.__send_lqc_command__(self.lqc, 'listChannels', False)
327
328

        index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
329
        message = self.__send_lqc_command__(self.lqc, 'deactivate', str(index))
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
        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
351
        channels = self.__send_lqc_command__(self.lqc, 'listChannels', False)
352
353
354

        index = channels.index(channel)
        # a activate channel
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
355
        message = self.__send_lqc_command__(self.lqc, 'activate', str(index))
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

        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
379
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel)
380
381
382
383
384
385

        rids = message.strip().split(' ')
        data['tracks'] = []
        for rid in rids:
            if rid != '':
                # get each rids metadata
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
386
                metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
387
388
389
390
391
392
393
394
395
396
                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
397
        channels = self.__send_lqc_command__(self.lqc, 'listChannels')
398
399
400
401
402
403
404

        """
        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
405
            status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(index + 1))
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
            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
440
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel, 'secondary_queue')
441
442
443
444
445
446
447
        rids = message.strip().split(' ')
        try:
            rid = rids[int(pos) - 1]
        except:
            self.warning('02')
            self.notifyClient()
            return
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
448
        message = self.__send_lqc_command__(self.lqc, 'remove', rid, channel)
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
        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
466
        data = self.__send_lqc_command__(self.lqc, 'seek', duration, channel)
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484

        # 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
485
        channels = self.__send_lqc_command__(self.lqc, 'listChannels')
486
487
488
489
490
491
492

        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
493
                    foundChannel = self.__send_lqc_command__(self.lqc, 'skip', 'mixer', str(index + 1))
494
495
496
497
498
499
500
501
502
503
504
                    break

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

        self.notifyClient()

505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
    def channel_activate(self, channel, activate):
        channels = self.get_all_channels()

        try:
            index = channels.index(channel)
            if len(channel) < 1:
                self.warning('02')
        except:
            self.error('03')

        else:
            message = self.__send_lqc_command__(self.lqc, "mixer", "select", index, activate)
            return message


520
521
522
523
524
525
526
527
528
529
    # ------------------------------------------------------------------------------------------ #
    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
        """
        # Liquidsoap Kommando
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
530
        #channels = self.__sendLqcCommand__(self.lqc, 'listChannels', False)
531
        channels = self.get_all_channels()
532

533
534
535
536
537
538
539
540
        try:
            index = channels.index(channel)
            if len(channel) < 1:
                self.warning('02')
        except:
            self.error('03')

        else:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
541
            message = self.__send_lqc_command__(self.lqc, 'mixer', 'volume', str(index), str(int(volume)))
542
            return message
543

544
545
546
547
#            if message.find('volume=' + str(volume) + '%'):
#                self.controller.success('01', str(volume))
#            else:
#                self.controller.warning('01')
548
549
550
551
552
553
554
555
556

    # ------------------------------------------------------------------------------------------ #
    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
557
        message = self.__send_lqc_command__(self.lqc, 'currentTrack')
558
559
560

        rid = message.strip()

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
561
        metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578

        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'
579

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
580
        channels = self.__send_lqc_command__(self.lqc, 'listChannels', False)
581
582
583
584
585

        index = channels.index(channel)
        state_data = {}
        try:
            index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
586
            status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(index + 1))
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
            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
617
        data = self.__send_lqc_command__(self.lqc, 'help')
618
619
620
621
622
623
624
625
626
627
628
629
630
        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
631
        channels = self.__send_lqc_command__(self.lqc, 'listChannels')
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648

        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
649
        data = self.__send_lqc_command__(self.lqc, 'playlistData')
650
651
652
653
654
655
656
657
658
659
660
        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
661
        data = self.__send_lqc_command__(self.lqc, 'flush')
662
663
664
665
666
667
668
669
        self.success('00')
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_insert(self, uri, pos):
        """
        Track in die Playlist einfuegen
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
670
        data = self.__send_lqc_command__(self.lqc, 'insert', uri, pos)
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
        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
706
707
            self.__send_lqc_command__(self.lqc, 'flush')
            data = self.__send_lqc_command__(self.lqc, 'loadPlaylist', filename)
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729

            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
730
        data = self.__send_lqc_command__(self.lqc, 'move', str(int(fromPos) + 1), str(int(toPos) + 1))
731
732
733
734
735
736
737
738
739
740
741
742
743

        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
744
        data = self.__send_lqc_command__(self.lqc, 'pause')
745
746
747
748
749
750
751
752
753
754
755
756
757
758

        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
759
        self.__send_lqc_command__(self.lqc, 'deactivate', '0')
760

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
761
        data = self.__send_lqc_command__(self.lqc, 'pause')
762
763
764
765
766
767
768

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

        self.notifyClient()
769
770

    # ------------------------------------------------------------------------------------------ #
771
772
773
774
775
776
777
    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
778
        self.__send_lqc_command__(self.lqc, 'activate', '0')
779
780
781
782

        if when == 'now':
            # immediately skip all playing channels
            # and activate the playlist channel
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
783
            channels = self.__send_lqc_command__(self.lqc, 'listChannels')
784
785
786
787
788
789
790
            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
791
                    status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(i + 1))
792
                    if "selected=true" in status:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
793
794
795
                        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')
796
797

        # send the play command
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
798
        data = self.__send_lqc_command__(self.lqc, 'play')
799
800
801
802
803
804
805
806
807
808
809
810
811
812
        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
813
        data = self.__send_lqc_command__(self.lqc, 'push', uri)
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828

        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
829
        data = self.__send_lqc_command__(self.lqc, 'remove', pos)
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844

        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
845
        data = self.__send_lqc_command__(self.lqc, 'seek', duration)
846
847
848
849
850
851
852
853
854
855
856
857
858
859

        # 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
860
        data = self.__send_lqc_command__(self.lqc, 'skip')
861
862
863
864
865
866
867
868
869
870

        self.success('00')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def version(self):
        """
        get version
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
871
        data = self.__send_lqc_command__(self.lqc, 'version')
872
873
        self.success('00', data)
        self.notifyClient()
874
875

    # ------------------------------------------------------------------------------------------ #
876
877
878
879
    def uptime(self):
        """
        get uptime
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
880
        data = self.__send_lqc_command__(self.lqc, 'uptime')
881
882
883
884
885
886
887
888
        self.success('00', data)
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def on_air(self):
        """
        get whats playing now
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
889
        data = self.__send_lqc_command__(self.lqc, 'on_air')
890
891
892
893
894
895
896
897
898
899
        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
900
        message = self.__send_lqc_command__(self.lqcr, 'start_record')
901
902
903
904
905
906
907
908
909
910
911
912
        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
913
        message = self.__send_lqc_command__(self.lqcr, 'stop_record')
914
915
916
917
918
919
920
921
922
923
924
925
926
927

        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
928
        message = self.__send_lqc_command__(self.lqcr, 'recorder_data')
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
        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
946
    def __send_lqc_command__(self, lqs_instance, namespace, command, *args):
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
        """
        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
962
            if not self.transaction:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
963
                self.open_conn(lqs_instance)
964

965
            if self.debug:
966
                print("LiquidSoapCommunicator is calling " + str(namespace) + "." + str(command) + str(args))
967

968
969
970
971
972
            # call wanted function ...
            func = getattr(lqs_instance, namespace)
            # ... and fetch the result
            result = func(command, *args)

973
974
975
976
977
978
            if self.debug:
                if len(args) == 0:
                    print("LiquidSoapCommunicator got response " + str(result))
                else:
                    print("LiquidSoapCommunicator got response " + str(result))

979
            # say byebye
980
            if not self.transaction:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
981
                self.close_conn(lqs_instance)
982
983
984

        except Exception as e:
            # Verbindung gescheitert - Fehler an Client
985
986
987
988
989
990
            #            if command.find('record') > -1:
            #                raise e
            # self.fatal('02')
            #            else:
            #                raise e
            # self.fatal('01')
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
991
            traceback.print_exc()
992
993
            raise e

994
            # Instanz/Thread zerstören - aufrufende Funktion wird nicht weiter abgearbeitet
995
            #del self
996
997
998
        else:
            return result

999
    # ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
1000
    def enable_transaction(self, connect=False):