communicator.py 35.5 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
from libraries.enum.scheduleentrytype import ScheduleEntryType
18

19

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

    # ------------------------------------------------------------------------------------------ #
31
    def __init__(self, config):
32
        """
33
34
35
36
37
38
        Constructor
        @type    lqs_socket: string
        @param   lqs_socket: Liquidsoap Player Socket
        @type    lqs_recsocket: string
        @param   lqs_recsocket: Liquidsoap Recorder Socket
        """
39
        self.config = config
40
41
42
        self.debug = config.get("debug")
        self.lqc = LiquidSoapPlayerClient(config, "simplestmixer.sock")
        self.lqcr = LiquidSoapRecorderClient(config, "record.sock")
43

44
45
46
        errors_file = self.config.get("install_dir") + "/errormessages/controller_error.js"
        self.error_data = simplejson.load(open(errors_file))

47
48
49
50
        #try:
        #    self.get_all_channels()
        #except LQConnectionError as lqe:
        #    print(lqe)
51

52
53
    #def set_controller(self, controller):
    #    self.controller = controller
54

55

56

57
58
59
    # ------------------------------------------------------------------------------------------ #
    def set_volume(self, mixernumber, volume):
        print("LiquidSoapCommunicator.set_volume")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
60
        return self.__send_lqc_command__(self.lqc, "mixer", "volume", mixernumber, volume)
61
62
63

    # ------------------------------------------------------------------------------------------ #
    def get_active_mixer(self):
64
65
66
67
        """
        get active mixer in liquidsoap server
        :return:
        """
68
69
        activeinputs = []

70
        # enable more control over the connection
71
        self.enable_transaction(True)
72

73
        inputs = self.get_all_channels()
74
75
76

        cnt = 0
        for input in inputs:
77
            status = self.__get_mixer_status__(cnt)
78
79
80
81
82
83

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

            cnt = cnt + 1

84
        self.disable_transaction(True)
85
86
87

        return activeinputs

88
89
90
91
92
93
    # ------------------------------------------------------------------------------------------ #
    def get_active_channel(self):
        """
        gets active channel from programme
        :return:
        """
94
95
        active_entry = self.scheduler.get_active_entry()
        if active_entry is None:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
96
            return ""
97
        return active_entry.type
98

99
100
    # ------------------------------------------------------------------------------------------ #
    def get_mixer_status(self):
101
102
        inputstate = {}

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
103
        self.enable_transaction()
104

105
        inputs = self.get_all_channels()
106
107
108

        cnt = 0
        for input in inputs:
109
            inputstate[input] = self.__get_mixer_status__(cnt)
110
111
            cnt = cnt + 1

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
112
        self.disable_transaction()
113
114
115

        return inputstate

116
    # ------------------------------------------------------------------------------------------ #
117
    def activate(self, entry):
118
119
        active_type = self.scheduler.get_active_entry().type
        if active_type == entry.type:
120
            print(TerminalColors.OK_PINK.value + "LiquidSoapCommunicator " + entry.type.value + " already active! Time: " + str(datetime.datetime.now()) + TerminalColors.ENDC.value)
121
122
123
124

            self.enable_transaction()

            if entry.type == ScheduleEntryType.FILESYSTEM:
125
                self.playlist_push(entry.source)
126
            if entry.type == ScheduleEntryType.STREAM:
127
                self.set_http_url(entry.source)
128
129

            self.disable_transaction()
130

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
131
            self.insert_track_service_entry(entry)
132

133
        else:
134
            print(TerminalColors.OK_PINK.value + "LiquidSoapCommunicator is activating " + entry.type.value + " & deactivating " + active_type.value + "! Time: " + str(datetime.datetime.now()) + TerminalColors.ENDC.value)
135

136
            self.enable_transaction()
137

138
139
            # push next song
            if entry.type == ScheduleEntryType.FILESYSTEM:
140
                self.playlist_push(entry.source)
141
            if entry.type == ScheduleEntryType.STREAM:
142
143
                self.set_http_url(entry.source)

144
            # set others to zero volume
145
            others = self.all_inputs_but(entry.type.value)
146
147
            for o in others:
                self.channel_volume(o, 0)
148
            self.channel_volume(entry.type.value, entry.volume)
149
150

            self.disable_transaction()
151

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
152
153
154
155
            self.insert_track_service_entry(entry)

    # ------------------------------------------------------------------------------------------ #
    def insert_track_service_entry(self, schedule_entry):
156
157
158
159
        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
160

161
        trackservice_entry.store(add=True, commit=True)
162
163
164

    # ------------------------------------------------------------------------------------------ #
    def all_inputs_but(self, input_type):
165
166
        activemixer_copy = self.get_all_channels().copy()

167
168
169
170
171
172
        try:
            activemixer_copy.remove(input_type)
        except ValueError:
            # Value not in list
            return activemixer_copy

173
174
175
176
        return activemixer_copy

    # ------------------------------------------------------------------------------------------ #
    def get_all_channels(self):
177
        if self.channels is None or self.channels == ['']:
178
179
180
            self.channels = self.__send_lqc_command__(self.lqc, "mixer", "inputs")

        return self.channels
181

182
183
184
185
186
    # ------------------------------------------------------------------------------------------ #
    def reload_channels(self):
        self.channels = None
        return self.get_all_channels()

187
188
    # ------------------------------------------------------------------------------------------ #
    def __get_mixer_status__(self, mixernumber):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
189
        return self.__send_lqc_command__(self.lqc, "mixer", "status", mixernumber)
190
191

    # ------------------------------------------------------------------------------------------ #
192
    def init_player(self):
193
        LiquidSoapInitThread(self).start()
194
        return "Started LiquidSoapInitThread!"
195
196
197
198
199
200
201
202
203
204
205
206
207

    # ------------------------------------------------------------------------------------------ #
    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

        """
208
        channels = self.__send_lqc_command__(self.lqc, 'list_channels')
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227

        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
228
        status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', '0')
229
230
231
232
233
234
235
236
237
238
239
240
241
242

        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
243
        remaining = self.__send_lqc_command__(self.lqc, 'playlist_remaining')
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
        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
281
        message = self.__send_lqc_command__(self.lqc, 'insert', uri, pos, channel)
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
        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
305
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel, 'secondary_queue')
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326

        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
327
        message = self.__send_lqc_command__(self.lqc, 'move', rid, str(int(toPos) - 1), channel)
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
        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'

351
        channels = self.__send_lqc_command__(self.lqc, 'list_channels', False)
352
353

        index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
354
        message = self.__send_lqc_command__(self.lqc, 'deactivate', str(index))
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
        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'

376
        channels = self.__send_lqc_command__(self.lqc, 'list_channels', False)
377
378
379

        index = channels.index(channel)
        # a activate channel
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
380
        message = self.__send_lqc_command__(self.lqc, 'activate', str(index))
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403

        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
404
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel)
405
406
407
408
409
410

        rids = message.strip().split(' ')
        data['tracks'] = []
        for rid in rids:
            if rid != '':
                # get each rids metadata
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
411
                metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
412
413
414
415
416
417
418
419
420
421
                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])
422
        channels = self.__send_lqc_command__(self.lqc, 'list_channels')
423
424
425
426
427
428
429

        """
        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
430
            status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(index + 1))
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
            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
465
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel, 'secondary_queue')
466
467
468
469
470
471
472
        rids = message.strip().split(' ')
        try:
            rid = rids[int(pos) - 1]
        except:
            self.warning('02')
            self.notifyClient()
            return
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
473
        message = self.__send_lqc_command__(self.lqc, 'remove', rid, channel)
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
        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
491
        data = self.__send_lqc_command__(self.lqc, 'seek', duration, channel)
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509

        # 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
510
        channels = self.__send_lqc_command__(self.lqc, 'list_channels')
511
512
513
514
515
516
517

        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
518
                    foundChannel = self.__send_lqc_command__(self.lqc, 'skip', 'mixer', str(index + 1))
519
520
521
522
523
524
525
526
527
528
529
                    break

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

        self.notifyClient()

530
    # ------------------------------------------------------------------------------------------ #
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
    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


546
547
548
549
550
551
552
553
554
555
    # ------------------------------------------------------------------------------------------ #
    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
556
        #channels = self.__sendLqcCommand__(self.lqc, 'list_channels', False)
557
        channels = self.get_all_channels()
558

559
560
561
        try:
            index = channels.index(channel)
            if len(channel) < 1:
562
                self.warning("02")
563
        except:
564
            self.error("03")
565
566

        else:
567
            message = self.__send_lqc_command__(self.lqc, "mixer", "volume", str(index), str(int(volume)))
568
            return message
569

570
571
572
573
#            if message.find('volume=' + str(volume) + '%'):
#                self.controller.success('01', str(volume))
#            else:
#                self.controller.warning('01')
574
575
576
577
578
579
580
581
582

    # ------------------------------------------------------------------------------------------ #
    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
583
        message = self.__send_lqc_command__(self.lqc, 'currentTrack')
584
585
586

        rid = message.strip()

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
587
        metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604

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

606
        channels = self.__send_lqc_command__(self.lqc, 'list_channels', False)
607
608
609
610
611

        index = channels.index(channel)
        state_data = {}
        try:
            index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
612
            status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(index + 1))
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
            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
643
        data = self.__send_lqc_command__(self.lqc, 'help')
644
645
646
647
648
649
650
651
652
653
654
655
656
        if not data:
            self.warning('01')
        else:
            self.success('00', data)
        self.notifyClient()

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

657
        channels = self.__send_lqc_command__(self.lqc, 'list_channels')
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674

        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
675
        data = self.__send_lqc_command__(self.lqc, 'playlistData')
676
677
678
679
680
681
682
683
684
685
686
        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
687
        data = self.__send_lqc_command__(self.lqc, 'flush')
688
689
690
691
692
693
694
695
        self.success('00')
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_insert(self, uri, pos):
        """
        Track in die Playlist einfuegen
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
696
        data = self.__send_lqc_command__(self.lqc, 'insert', uri, pos)
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
        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
732
733
            self.__send_lqc_command__(self.lqc, 'flush')
            data = self.__send_lqc_command__(self.lqc, 'loadPlaylist', filename)
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755

            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
756
        data = self.__send_lqc_command__(self.lqc, 'move', str(int(fromPos) + 1), str(int(toPos) + 1))
757
758
759
760
761
762
763
764
765
766
767
768
769

        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
770
        data = self.__send_lqc_command__(self.lqc, 'pause')
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_stop(self):
        """
        Playlist stoppen - der Kanal wird deaktiviert
        """
        # Kanal 0 (Playlist) deaktivieren
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
785
        self.__send_lqc_command__(self.lqc, 'deactivate', '0')
786

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
787
        data = self.__send_lqc_command__(self.lqc, 'pause')
788
789
790
791
792
793
794

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

        self.notifyClient()
795
796

    # ------------------------------------------------------------------------------------------ #
797
798
799
800
801
802
803
    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
804
        self.__send_lqc_command__(self.lqc, 'activate', '0')
805
806
807
808

        if when == 'now':
            # immediately skip all playing channels
            # and activate the playlist channel
809
            channels = self.__send_lqc_command__(self.lqc, 'list_channels')
810
811
812
813
814
815
816
            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
817
                    status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(i + 1))
818
                    if "selected=true" in status:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
819
820
821
                        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')
822
823

        # send the play command
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
824
        data = self.__send_lqc_command__(self.lqc, 'play')
825
826
827
828
829
830
831
        if not self._check_result(data):
            self.info('01')
        else:
            self.success('00')

        self.notifyClient()

832
833
834
835
    # ------------------------------------------------------------------------------------------ #
    def set_http_url(self, uri):
        return self.__send_lqc_command__(self.lqc, 'http', 'url', uri)

836
837
838
839
840
841
842
    # ------------------------------------------------------------------------------------------ #
    def playlist_push(self, uri):
        """
        Eine Uri in die Playlist einfügen
        @type   uri:   str
        @param  uri:   Die Uri
        """
843
844
        return self.__send_lqc_command__(self.lqc, 'fs', 'push', uri)
#        self.notifyClient()
845
846
847
848
849
850
851
852

    # ------------------------------------------------------------------------------------------ #
    def playlist_remove(self, pos):
        """
        Playlist-Eintrag löschen
        @type     pos:     int
        @param    pos:     Position des Eintrags
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
853
        data = self.__send_lqc_command__(self.lqc, 'remove', pos)
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868

        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
869
        data = self.__send_lqc_command__(self.lqc, 'seek', duration)
870
871
872
873
874
875
876
877
878
879
880
881
882
883

        # 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
884
        data = self.__send_lqc_command__(self.lqc, 'skip')
885
886
887
888
889
890
891
892
893
894

        self.success('00')

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def version(self):
        """
        get version
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
895
        data = self.__send_lqc_command__(self.lqc, 'version')
896
897
        self.success('00', data)
        self.notifyClient()
898
899

    # ------------------------------------------------------------------------------------------ #
900
901
902
903
    def uptime(self):
        """
        get uptime
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
904
        data = self.__send_lqc_command__(self.lqc, 'uptime')
905
906
907
908
909
910
911
912
        self.success('00', data)
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def on_air(self):
        """
        get whats playing now
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
913
        data = self.__send_lqc_command__(self.lqc, 'on_air')
914
915
916
917
918
919
920
921
922
923
        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
924
        message = self.__send_lqc_command__(self.lqcr, 'start_record')
925
926
927
928
929
930
931
932
933
934
935
936
        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
937
        message = self.__send_lqc_command__(self.lqcr, 'stop_record')
938
939
940
941
942
943
944
945
946
947
948
949
950
951

        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
952
        message = self.__send_lqc_command__(self.lqcr, 'recorder_data')
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
        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
970
    def __send_lqc_command__(self, lqs_instance, namespace, command, *args):
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
        """
        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
986
            if not self.transaction:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
987
                self.open_conn(lqs_instance)
988

989
            if self.debug:
990
                print("LiquidSoapCommunicator is calling " + str(namespace) + "." + str(command) + str(args))
991

992
993
994
995
996
            # call wanted function ...
            func = getattr(lqs_instance, namespace)
            # ... and fetch the result
            result = func(command, *args)

997
            if self.debug:
998
                print("LiquidSoapCommunicator got response " + str(result))
999

1000
            # say byebye