communicator.py 38.9 KB
Newer Older
1
2
3
4
5
import os
import codecs
import urllib
import tempfile
import simplejson
6
import logging
7

8
from modules.base.parsexml import parsexml
9
10
11
from modules.communication.liquidsoap.playerclient import LiquidSoapPlayerClient
from modules.communication.liquidsoap.recorderclient import LiquidSoapRecorderClient
from modules.communication.liquidsoap.initthread import LiquidSoapInitThread
12
13
from modules.communication.mail.mail import AuraMailer

14
15
from libraries.enum.consolecolor import TerminalColors
from libraries.exceptions.auraexceptions import LQConnectionError
16
from libraries.database.broadcasts import TrackService
17
from libraries.exceptions.exception_logger import ExceptionLogger
18
from libraries.enum.scheduleentrytype import ScheduleEntryType
19

20

21
class LiquidSoapCommunicator(ExceptionLogger):
22
23
    lqc = None
    lqcr = None
24
    logger = None
25
    transaction = 0
26
    channels = None
27
    scheduler = None
28
    error_data = None
29
30
    auramailer = None
    aborttransaction = False
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
31
    connection_attempts = 0
32
33

    # ------------------------------------------------------------------------------------------ #
34
    def __init__(self, config):
35
        """
36
37
38
39
40
41
        Constructor
        @type    lqs_socket: string
        @param   lqs_socket: Liquidsoap Player Socket
        @type    lqs_recsocket: string
        @param   lqs_recsocket: Liquidsoap Recorder Socket
        """
42
        self.config = config
43
44
        self.logger = logging.getLogger("AuraEngine")

45
        self.lqc = LiquidSoapPlayerClient(config, "engine.sock")
46
        self.lqcr = LiquidSoapRecorderClient(config, "record.sock")
47

48
        errors_file = self.config.get("install_dir") + "/errormessages/controller_error.js"
49
50
51
        f = open(errors_file)
        self.error_data = simplejson.load(f)
        f.close()
52

53
        self.auramailer = AuraMailer(config.get("admin_mail"), config.get("from_mail"))
54

55
56
    # ------------------------------------------------------------------------------------------ #
    def set_volume(self, mixernumber, volume):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
57
        return self.__send_lqc_command__(self.lqc, "mixer", "volume", mixernumber, volume)
58
59
60

    # ------------------------------------------------------------------------------------------ #
    def get_active_mixer(self):
61
62
63
64
        """
        get active mixer in liquidsoap server
        :return:
        """
65
66
        activeinputs = []

67
        # enable more control over the connection
68
        self.enable_transaction()
69

70
        inputs = self.get_all_channels()
71
72
73

        cnt = 0
        for input in inputs:
74
            status = self.__get_mixer_status__(cnt)
75
76
77
78
79
80

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

            cnt = cnt + 1

81
        self.disable_transaction()
82
83
84

        return activeinputs

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

96
97
    # ------------------------------------------------------------------------------------------ #
    def get_mixer_status(self):
98
99
        inputstate = {}

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
100
        self.enable_transaction()
101

102
        inputs = self.get_all_channels()
103
104
105

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

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
109
        self.disable_transaction()
110
111
112

        return inputstate

113
114
115
116
117
118
119
120
    # ------------------------------------------------------------------------------------------ #
    def get_recorder_status(self):
        self.enable_transaction(self.lqcr)
        recorder_state = self.__send_lqc_command__(self.lqcr, "record", "status")
        self.disable_transaction(self.lqcr)

        return recorder_state

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
    # ------------------------------------------------------------------------------------------ #
    def http_start_stop(self, start):
        if start:
            cmd = "start"
        else:
            cmd = "stop"

        try:
            self.enable_transaction()
            self.__send_lqc_command__(self.lqc, "http", cmd)
            self.disable_transaction()
        except LQConnectionError as e:
            # we already caught and handled this error in __send_lqc_command__, but we do not want to execute this function further
            pass


137
    # ------------------------------------------------------------------------------------------ #
138
    def activate(self, entry):
139
140
        active_type = self.scheduler.get_active_entry().type

141
142
        try:
            # enable transaction
143
144
            self.enable_transaction()

145
146
147
148
149
150
            if active_type == entry.type:
                # push something to active channel
                self.activate_same_channel(entry)
            else:
                # switch to another channel
                self.activate_different_channel(entry, active_type)
151

152
            # disable conn
153
            self.disable_transaction()
154

155
            # insert playlist entry
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
156
            self.insert_track_service_entry(entry)
157
158
159
        except LQConnectionError as e:
            # we already caught and handled this error in __send_lqc_command__, but we do not want to execute this function further
            pass
160

161
162
163
164
165
166
167
168
169
170
171
172
    # ------------------------------------------------------------------------------------------ #
    def activate_same_channel(self, entry, silent=False):
        if not silent:
            self.logger.info(TerminalColors.PINK.value + entry.type.value + " already active!" + TerminalColors.ENDC.value)

        # push to fs or stream
        if entry.type == ScheduleEntryType.FILESYSTEM:
            self.playlist_push(entry.source)
        if entry.type == ScheduleEntryType.STREAM:
            self.http_start_stop(True)
            self.set_http_url(entry.source)
        # nothing to do when we are live => just leave it as is
173

174
175
176
    # ------------------------------------------------------------------------------------------ #
    def activate_different_channel(self, entry, active_type):
        self.logger.info(TerminalColors.PINK.value + "LiquidSoapCommunicator is activating " + entry.type.value + " & deactivating " + active_type.value + "!" + TerminalColors.ENDC.value)
177

178
        self.activate_same_channel(entry, True)
179

180
181
182
183
184
185
        # set others to zero volume
        others = self.all_inputs_but(entry.type.value)
        for o in others:
            self.channel_volume(o, 0)
        # set active channel to wanted volume
        self.channel_volume(entry.type.value, entry.volume)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
186
187
188

    # ------------------------------------------------------------------------------------------ #
    def insert_track_service_entry(self, schedule_entry):
189
190
191
192
        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
193

194
        trackservice_entry.store(add=True, commit=True)
195
196
197

    # ------------------------------------------------------------------------------------------ #
    def all_inputs_but(self, input_type):
198
        try:
199
            activemixer_copy = self.get_all_channels().copy()
200
201
            activemixer_copy.remove(input_type)
        except ValueError:
202
203
204
            self.logger.error("Requested channel not in channellist ")
        except AttributeError:
            self.logger.critical("Channellist is None")
205

206
207
208
209
        return activemixer_copy

    # ------------------------------------------------------------------------------------------ #
    def get_all_channels(self):
210
        if self.channels is None or len(self.channels) == 0:
211
212
213
            self.channels = self.__send_lqc_command__(self.lqc, "mixer", "inputs")

        return self.channels
214

215
216
217
218
219
    # ------------------------------------------------------------------------------------------ #
    def reload_channels(self):
        self.channels = None
        return self.get_all_channels()

220
221
    # ------------------------------------------------------------------------------------------ #
    def __get_mixer_status__(self, mixernumber):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
222
        return self.__send_lqc_command__(self.lqc, "mixer", "status", mixernumber)
223
224

    # ------------------------------------------------------------------------------------------ #
225
    def init_player(self):
226
227
228
229
        t = LiquidSoapInitThread()
        t.liquidsoapcommunicator = self
        t.active_entry = self.scheduler.get_active_entry()
        t.start()
230
        return "LiquidSoapInitThread started!"
231
232
233
234
235
236
237
238
239

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

        """
240
        channels = self.__send_lqc_command__(self.lqc, 'list_channels')
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259

        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
260
        status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', '0')
261
262
263
264
265
266
267
268
269
270
271
272
273
274

        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
275
        remaining = self.__send_lqc_command__(self.lqc, 'playlist_remaining')
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
        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
313
        message = self.__send_lqc_command__(self.lqc, 'insert', uri, pos, channel)
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
        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
337
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel, 'secondary_queue')
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358

        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
359
        message = self.__send_lqc_command__(self.lqc, 'move', rid, str(int(toPos) - 1), channel)
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
        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'

383
        channels = self.__send_lqc_command__(self.lqc, 'list_channels', False)
384
385

        index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
386
        message = self.__send_lqc_command__(self.lqc, 'deactivate', str(index))
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
        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'

408
        channels = self.__send_lqc_command__(self.lqc, 'list_channels', False)
409
410
411

        index = channels.index(channel)
        # a activate channel
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
412
        message = self.__send_lqc_command__(self.lqc, 'activate', str(index))
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435

        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
436
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel)
437
438
439
440
441
442

        rids = message.strip().split(' ')
        data['tracks'] = []
        for rid in rids:
            if rid != '':
                # get each rids metadata
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
443
                metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
444
445
446
447
448
449
450
451
452
453
                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])
454
        channels = self.__send_lqc_command__(self.lqc, 'list_channels')
455
456
457
458
459
460
461

        """
        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
462
            status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(index + 1))
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
            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
497
        message = self.__send_lqc_command__(self.lqc, 'get_queue', channel, 'secondary_queue')
498
499
500
501
502
503
504
        rids = message.strip().split(' ')
        try:
            rid = rids[int(pos) - 1]
        except:
            self.warning('02')
            self.notifyClient()
            return
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
505
        message = self.__send_lqc_command__(self.lqc, 'remove', rid, channel)
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
        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
523
        data = self.__send_lqc_command__(self.lqc, 'seek', duration, channel)
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541

        # 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
542
        channels = self.__send_lqc_command__(self.lqc, 'list_channels')
543
544
545
546
547
548
549

        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
550
                    foundChannel = self.__send_lqc_command__(self.lqc, 'skip', 'mixer', str(index + 1))
551
552
553
554
555
556
557
558
559
560
561
                    break

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

        self.notifyClient()

562
    # ------------------------------------------------------------------------------------------ #
563
564
565
566
567
568
569
570
    def channel_activate(self, channel, activate):
        channels = self.get_all_channels()

        try:
            index = channels.index(channel)
            if len(channel) < 1:
                self.warning('02')
        except:
571
572
            import traceback
            traceback.print_exc()
573
574
575
576
577
578
579
            self.error('03')

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


580
581
582
    # ------------------------------------------------------------------------------------------ #
    def channel_volume(self, channel, volume):
        """
583
        set volume of a channel
584
        @type     channel:  string
585
        @param    channel:  Channel
586
        @type     volume:   int
587
        @param    volume:   Volume between 0 and 100
588
        """
589

590
        try:
591
            channels = self.get_all_channels()
592
            index = channels.index(channel)
593

594
            if len(channel) < 1:
595
596
597
                self.warning(job="channel_volume", errnum="02")
            else:
                message = self.__send_lqc_command__(self.lqc, "mixer", "volume", str(index), str(int(volume)))
598

599
600
601
602
                if message.find('volume=' + str(volume) + '%'):
                    self.success("channel_volume", errnum="00", value=str(volume))
                else:
                    self.warning("channel_volume", errnum="01")
603

604
605
606
607
                return message
        except (AttributeError, ValueError): #(LQConnectionError, AttributeError):
            self.disable_transaction(force=True)
            self.error("channel_volume", errnum="03")
608
609
610
611
612
613
614
615
616

    # ------------------------------------------------------------------------------------------ #
    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
617
        message = self.__send_lqc_command__(self.lqc, 'currentTrack')
618
619
620

        rid = message.strip()

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
621
        metadata = self.__send_lqc_command__(self.lqc, 'getMetadata', rid)
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638

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

640
        channels = self.__send_lqc_command__(self.lqc, 'list_channels', False)
641
642
643
644
645

        index = channels.index(channel)
        state_data = {}
        try:
            index = channels.index(channel)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
646
            status = self.__send_lqc_command__(self.lqc, 'status', 'mixer', str(index + 1))
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
            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
        """
        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
675
        data = self.__send_lqc_command__(self.lqc, 'help')
676
677
678
679
680
681
682
683
684
685
686
687
688
        if not data:
            self.warning('01')
        else:
            self.success('00', data)
        self.notifyClient()

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

689
        channels = self.__send_lqc_command__(self.lqc, 'list_channels')
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706

        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
707
        data = self.__send_lqc_command__(self.lqc, 'playlistData')
708
709
710
711
712
713
714
715
716
717
718
        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
719
        data = self.__send_lqc_command__(self.lqc, 'flush')
720
721
722
723
724
725
726
727
        self.success('00')
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_insert(self, uri, pos):
        """
        Track in die Playlist einfuegen
        """
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
728
        data = self.__send_lqc_command__(self.lqc, 'insert', uri, pos)
729
730
731
732
733
734
735
736
737
738
739
740
741
742
        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:
743
            xml = urllib.urlopen(uri).read().decode("utf8")
744
745
746

        except:
            try:
747
                xml = open(uri).read().decode("utf8")
748
            except:
749
                self.error("01", self.lq_error["message"])
750
751
752
753
754
                self.notifyClient()
                return

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

755
        with codecs.open(filename, "w", encoding="utf8") as text_file:
756
757
758
759
760
            text_file.write(xml)

        playlist = parsexml(xml)

        if not isinstance(playlist, dict):
761
            self.error("02")
762
763
            self.notifyClient()
        else:
764
765
            self.__send_lqc_command__(self.lqc, "flush")
            data = self.__send_lqc_command__(self.lqc, "loadPlaylist", filename)
766
767

            if not self._check_result(data):
768
                self.error("01", self.lq_error["message"])
769
770
771
772

            else:
                os.remove(filename)
                self._updateEventQueue(playlist)
773
774
775
                event = {"job": "loadplaylist", "uri": uri}
                self.messenger.fire_event("loadplaylist", event, "player")
                self.success("00")
776
777
778
779
780
781
782
783
784
785
786
787

            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
        """
788
        data = self.__send_lqc_command__(self.lqc, "move", str(int(fromPos) + 1), str(int(toPos) + 1))
789
790

        if not self._check_result(data):
791
            self.warning("01")
792
        else:
793
            self.success("00")
794
795
796
797
798
799
800
801

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_pause(self):
        """
        Playlist pausieren
        """
802
        data = self.__send_lqc_command__(self.lqc, "pause")
803
804

        if not self._check_result(data):
805
            self.info("01")
806
        else:
807
            self.success("00")
808
809
810
811
812
813
814
815
816

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_stop(self):
        """
        Playlist stoppen - der Kanal wird deaktiviert
        """
        # Kanal 0 (Playlist) deaktivieren
817
        self.__send_lqc_command__(self.lqc, "deactivate", "0")
818

819
        data = self.__send_lqc_command__(self.lqc, "pause")
820
821

        if not self._check_result(data):
822
            self.info("01")
823
        else:
824
            self.success("00")
825
826

        self.notifyClient()
827
828

    # ------------------------------------------------------------------------------------------ #
829
    def playlist_play(self, when="now"):
830
831
832
833
834
835
        """
        Playlist starten
        @type   when:   string
        @param  when:   Wenn "now" werden alle anderen Kanäle deaktiviert und geskipped
        """
        # Playlist Kanal aktivieren
836
        self.__send_lqc_command__(self.lqc, "activate", "0")
837

838
        if when == "now":
839
840
            # immediately skip all playing channels
            # and activate the playlist channel
841
            channels = self.__send_lqc_command__(self.lqc, "list_channels")
842
            if not isinstance(channels, list):
843
                self.error("03")
844
            elif len(channels) < 1:
845
                self.warning("02")
846
847
848
            else:
                # xrange
                for i in range(len(channels)):
849
                    status = self.__send_lqc_command__(self.lqc, "status", "mixer", str(i + 1))
850
                    if "selected=true" in status:
851
852
853
                        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")
854
855

        # send the play command
856
        data = self.__send_lqc_command__(self.lqc, "play")
857
        if not self._check_result(data):
858
            self.info("01")
859
        else:
860
            self.success("00")
861
862
863

        self.notifyClient()

864
865
    # ------------------------------------------------------------------------------------------ #
    def set_http_url(self, uri):
866
        return self.__send_lqc_command__(self.lqc, "http", "url", uri)
867

868
869
870
871
872
873
874
    # ------------------------------------------------------------------------------------------ #
    def playlist_push(self, uri):
        """
        Eine Uri in die Playlist einfügen
        @type   uri:   str
        @param  uri:   Die Uri
        """
875
        return self.__send_lqc_command__(self.lqc, "fs", "push", uri)
876
#        self.notifyClient()
877
878
879
880
881
882
883
884

    # ------------------------------------------------------------------------------------------ #
    def playlist_remove(self, pos):
        """
        Playlist-Eintrag löschen
        @type     pos:     int
        @param    pos:     Position des Eintrags
        """
885
        data = self.__send_lqc_command__(self.lqc, "remove", pos)
886
887

        if not self._check_result(data):
888
            self.info("01")
889
        else:
890
            self.success("00")
891
892
893
894
895
896
897
898
899
900

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_seek(self, duration):
        """
        Im aktuell spielenden Track auf dem der Playlist "vorspulen"
        @type     duration: int
        @param    duration: Dauer in Sekunden
        """
901
        data = self.__send_lqc_command__(self.lqc, "seek", duration)
902
903
904

        # Resultate prüfen
        if self._check_result(data):
905
            self.success("00", self.lq_error["value"])
906
        else:
907
            self.warning("01")
908
909
910
911
912
913
914
915

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def playlist_skip(self):
        """
        Playlist skippen
        """
916
        data = self.__send_lqc_command__(self.lqc, "skip")
917

918
        self.success("00")
919
920
921
922
923
924
925
926

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def version(self):
        """
        get version
        """
927
928
        data = self.__send_lqc_command__(self.lqc, "version")
        self.success("00", data)
929
        self.notifyClient()
930
931

    # ------------------------------------------------------------------------------------------ #
932
933
934
935
    def uptime(self):
        """
        get uptime
        """
936
937
        data = self.__send_lqc_command__(self.lqc, "uptime")
        self.success("00", data)
938
939
940
941
942
943
944
        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def on_air(self):
        """
        get whats playing now
        """
945
946
        data = self.__send_lqc_command__(self.lqc, "on_air")
        self.success("00", data)
947
948
        self.notifyClient()

949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
        # ------------------------------------------------------------------------------------------ #
        def http_start_stop(self, start):
            if start:
                cmd = "start"
            else:
                cmd = "stop"

            try:
                self.enable_transaction()
                self.__send_lqc_command__(self.lqc, "http", cmd)
                self.disable_transaction()
            except LQConnectionError as e:
                # we already caught and handled this error in __send_lqc_command__, but we do not want to execute this function further
                pass

964
965
966
967
968
969
970
    # ------------------------------------------------------------------------------------------ #
    def recorder_start(self):
        """
        Recorder starten
        @rtype:   string
        @return:  Die Antwort des Liquidsoap-Servers
        """
971
972
973
        message = self.__send_lqc_command__(self.lqcr, "start_record")
        if message.strip() == "OK":
            self.success("00")
974
        else:
975
            self.warning("01")
976
977
978
979
980
981
982
983

        self.notifyClient()

    # ------------------------------------------------------------------------------------------ #
    def recorder_stop(self):
        """
        Recorder stoppen
        """
984
        message = self.__send_lqc_command__(self.lqcr, "stop_record")
985

986
987
        if message.strip() == "OK":
            self.success("00")
988
        else:
989
            self.warning("01")
990
991
992
993
994
995
996
997
998

        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
        """
999
1000
        message = self.__send_lqc_command__(self.lqcr, "recorder_data")
        l = message.split(",")