broadcasts.py 14.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
#  engine
#
#  Playout Daemon for autoradio project
#
#
#  Copyright (C) 2017-2018 Gottfried Gaisbauer <gottfried.gaisbauer@servus.at>
#
#  This file is part of engine.
#
#  engine is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  any later version.
#
#  engine is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with engine. If not, see <http://www.gnu.org/licenses/>.
#
24

25
import sys
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
26
import time
27
28
import logging
import datetime
29

30
from sqlalchemy import orm, func, Boolean, Column, DateTime, Integer, String, ForeignKey, ForeignKeyConstraint
31
from sqlalchemy.orm import relationship
32
from sqlalchemy.sql.expression import false, true
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
33
from libraries.database.database import DB
34
from libraries.enum.auraenumerations import ScheduleEntryType
35

36

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
37
class AuraDatabaseModel:
38
39
40
41
42
    logger = None

    def __init__(self):
        self.logger = logging.getLogger("AuraEngine")

43
44
    def store(self, add=False, commit=False):
        if add:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
45
            DB.session.add(self)
46
        if commit:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
47
            DB.session.commit()
48
49

    def delete(self, commit=False):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
50
        DB.session.delete(self)
51
52
53
54
#        current_db_sessions = DB.session.object_session(self)
#        current_db_sessions.delete(self)
#        return
#        DB.session.delete(self)
55
        if commit:
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
56
            DB.session.commit()
57

58
59
    def _asdict(self):
        return self.__dict__
60

61
62
    @staticmethod
    def recreate_db(systemexit = False):
63
64
        manualschedule = Schedule()
        manualschedule.schedule_id = 0
65
        manualschedule.show_name = "Manual Show"
66

67
#        self.logger.debug("Recreating Database...")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
68
        DB.drop_all()
69
#        self.logger.debug("all dropped. creating...")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
70
        DB.create_all()
71
#        self.logger.debug("inserting manual scheduling possibility and fallback trackservice schedule")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
72
        DB.session.add(manualschedule)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
73
#        db.session.add(fallback_trackservice_schedule)
74
#        self.logger.debug("all created. commiting...")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
75
        DB.session.commit()
76
#        self.logger.debug("Database recreated!")
77

78
79
        if systemexit:
            sys.exit(0)
80

81

82
# ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
83
class Schedule(DB.Model, AuraDatabaseModel):
84
    """
85
    One specific Schedule for a show on a timeslot
86
    """
87
    __tablename__ = 'schedule'
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
88
89

    # primary and foreign keys
90
    schedule_id = Column(Integer, primary_key=True, autoincrement=False)
91
    show_id = Column(Integer) # well, in fact not needed..
92

93
94
    schedule_start = Column(DateTime) # can be null due to manual entries
    schedule_end = Column(DateTime) # can be null due to manual entries
95
96
    show_name = Column(String(256))
    show_hosts = Column(String(256))
97
    funding_category = Column(String(256))
98
99
100
101
102
103
    comment = Column(String(512))
    languages = Column(String(256))
    type = Column(String(256))
    category = Column(String(256))
    topic = Column(String(256))
    musicfocus = Column(String(256))
104
105
106

    is_repetition = Column(Boolean())

107
108
109
110
111
    playlist_id = Column(Integer)
    timeslot_fallback_id = Column(Integer)
    show_fallback_id = Column(Integer)
    station_fallback_id = Column(Integer)

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
112
113
114
115
116
117
118
119
120
121
122
    @staticmethod
    def select_all():
        # fetching all entries
        all_entries = DB.session.query(Schedule).filter().order_by(Schedule.schedule_start).all()
        return all_entries

    @staticmethod
    def select_by_id(id):
        entry = DB.session.query(Schedule).filter(Schedule.schedule_id == id).first()
        return entry

123
124
125
126
127
128
    def get_length(self):
        sec1 = int(datetime.datetime.strptime(self.start[0:16].replace(" ", "T"), "%Y-%m-%dT%H:%M").strftime("%s"))
        sec2 = int(datetime.datetime.strptime(self.end[0:16].replace(" ", "T"), "%Y-%m-%dT%H:%M").strftime("%s"))
        len = sec2 - sec1
        return len

129

130
# ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
131
class ScheduleEntry(DB.Model, AuraDatabaseModel):
132
    """
133
    One schedule can have multiple entries
134
    """
135
136
    __tablename__ = 'schedule_entry'

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
137
    # primary and foreign keys
138
139
140
    playlist_id = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
    entry_num = Column(Integer, primary_key=True, nullable=False, autoincrement=False)
    schedule_id = Column(Integer, ForeignKey("schedule.schedule_id"))
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
141

142
    entry_start = Column(DateTime)
143
    source = Column(String(256))
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
144
    volume = Column(Integer, default=100)
145
    fallback_type = Column(Integer, default=0)
146
    cleansource = ""
147
    cleanprotocol = ""
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
148
    entry_start_unix = 0
149
    programme_index = -1
150
    type = None
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
151

152
    schedule = relationship("Schedule", foreign_keys=[schedule_id], lazy="joined")
153

154
    # normal constructor
155
156
    def __init__(self, **kwargs):
        super(ScheduleEntry, self).__init__(**kwargs)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
157
        self.calc_unix_times()
158
        self.define_clean_source()
159

160
    # constructor like - called from sqlalchemy
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
161
    @orm.reconstructor
162
163
    def reconstructor(self):
        self.calc_unix_times()
164
        self.define_clean_source()
165
166
        self.set_entry_type()

167
    def define_clean_source(self):
168
169
        if self.source.startswith("http"):
            self.cleanprotocol = self.source[:7]
170
            self.cleansource = self.source
171
172
173
174
175
176
177

        elif self.source.startswith("linein"):
            self.cleanprotocol = self.source[:9]
            self.cleansource = self.source[9:]

        elif self.source.startswith("pool") or self.source.startswith("file") or self.source.startswith("live"):
            self.cleanprotocol = self.source[:7]
178
            self.cleansource = self.source[7:]
179
180
181

        elif self.source.startswith("playlist"):
            self.cleanprotocol = self.source[:11]
182
183
            self.cleansource = self.source[11:]

184
185
186
        else:
            self.logger.error("Unknown source protocol")

187
    def calc_unix_times(self):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
188
189
        if self.entry_start is not None:
            self.entry_start_unix = time.mktime(self.entry_start.timetuple())
190

191
192
    def set_entry_type(self):
        if self.source.startswith("http"):
193
194
195
196
            self.type = ScheduleEntryType.STREAM
        if self.source.startswith("pool") or self.source.startswith("playlist") or self.source.startswith("file"):
            self.type = ScheduleEntryType.FILESYSTEM
        if self.source.startswith("live") or self.source.startswith("linein"):
197
198
199
200
201
202
203
204
205
206
            if self.cleansource == "0":
                self.type = ScheduleEntryType.LIVE_0
            elif self.cleansource == "1":
                self.type = ScheduleEntryType.LIVE_1
            elif self.cleansource == "2":
                self.type = ScheduleEntryType.LIVE_2
            elif self.cleansource == "3":
                self.type = ScheduleEntryType.LIVE_3
            elif self.cleansource == "4":
                self.type = ScheduleEntryType.LIVE_4
207

208
209
210
    # ------------------------------------------------------------------------------------------ #
    @staticmethod
    def select_all():
211
        # fetching all entries
212
        all_entries = DB.session.query(ScheduleEntry).filter(ScheduleEntry.fallback_type == 0).order_by(ScheduleEntry.entry_start).all()
213
214
215
216
217
218
219
220

        cnt = 0
        for entry in all_entries:
            entry.programme_index = cnt
            cnt = cnt + 1

        return all_entries

221
222
223
224
225
226
227
228
229
230
231
232
233
    @staticmethod
    def select_act_programme(include_act_playing = True):
        # fetching all from today to ..
        today = datetime.date.today()
        all_entries = DB.session.query(ScheduleEntry).filter(ScheduleEntry.entry_start >= today, ScheduleEntry.fallback_type == 0).order_by(ScheduleEntry.entry_start).all()

        cnt = 0
        for entry in all_entries:
            entry.programme_index = cnt
            cnt = cnt + 1

        return all_entries

234
235
236
237
238
239
240
241
242
243
    # ------------------------------------------------------------------------------------------ #
    @staticmethod
    def truncate():
        all_entries = DB.session.query(ScheduleEntry).filter().order_by(ScheduleEntry.entry_start).all()

        for a in all_entries:
            a.delete()
        DB.session.commit()

    # ------------------------------------------------------------------------------------------ #
244
245
246
    @staticmethod
    def select_next_manual_entry_num():

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
247
        max_manual_entry_num = DB.session.query(func.max(ScheduleEntry.entry_num)).filter(ScheduleEntry.schedule_id == 0).first()
248
249
250
251
252
253
254
255

        if max_manual_entry_num[0] is None:
            return 0
        else:
            return int(max_manual_entry_num[0])+1

    # ------------------------------------------------------------------------------------------ #
    @staticmethod
256
257
    def select_upcoming(datefrom=datetime.datetime.now()):
        upcomingtracks = DB.session.query(ScheduleEntry).filter(ScheduleEntry.entry_start > datefrom).order_by(ScheduleEntry.entry_start).all()
258
259
260
261
262
        return upcomingtracks

    # ------------------------------------------------------------------------------------------ #
    @staticmethod
    def select_one(playlist_id, entry_num):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
263
        return DB.session.query(ScheduleEntry).filter(ScheduleEntry.playlist_id == playlist_id, ScheduleEntry.entry_num == entry_num).first()
264

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
265
266
267
268
269
    # ------------------------------------------------------------------------------------------ #
    @staticmethod
    def select_playlist(playlist_id):
        return DB.session.query(ScheduleEntry).filter(ScheduleEntry.playlist_id == playlist_id).all()

270
271
272
273
    def getChannel(self):
        if self.type == self.type.FILESYSTEM:
            return "fs"

274
        if self.type == self.type.LIVE_0 or self.type == self.type.LIVE_1 or self.type == self.type.LIVE_2 or self.type == self.type.LIVE_3 or self.type == self.type.LIVE_4:
275
276
277
278
279
280
            return "aura_linein_"+self.cleansource # .cleanprotocol[8]

        if self.type == self.type.STREAM:
            return "http"


281
282
    # ------------------------------------------------------------------------------------------ #
    def __str__(self):
283
        return "ScheduleID: #" + str(self.schedule_id) + " Showname: " + self.schedule.show_name + " starts @ " + str(self.entry_start) + " and plays " + self.source
284

285

286
# ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
287
class TrackService(DB.Model, AuraDatabaseModel):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
288
    __tablename__ = 'trackservice'
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
289

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
290
291
292
    trackservice_id = Column(Integer, primary_key=True, autoincrement=True)
    playlist_id = Column(Integer, nullable=False)
    entry_num = Column(Integer, nullable=False)
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
293

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
294
    source = Column(String(255), nullable=False)
295
    start = Column(DateTime, nullable=False, default=func.now())
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
296

297
298
299
    __table_args__ = (
        ForeignKeyConstraint(['playlist_id', 'entry_num'], ['schedule_entry.playlist_id', 'schedule_entry.entry_num']),
    )
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
300

301
    #schedule = relationship("Schedule", foreign_keys=[schedule_id], lazy="joined")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
302
303
    # trackservice_entry = relationship("ScheduleEntry", foreign_keys=[playlist_id, entry_num], lazy="joined")
    schedule_entry = relationship("ScheduleEntry", primaryjoin="and_(TrackService.playlist_id==ScheduleEntry.playlist_id, TrackService.entry_num==ScheduleEntry.entry_num)", lazy="joined")
304

305
    @staticmethod
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
306
    # ------------------------------------------------------------------------------------------ #
307
    def select_one(trackservice_id):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
308
        return DB.session.query(TrackService).filter(TrackService.trackservice_id == trackservice_id).first()
309

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
310
311
312
313
314
315
316
    @staticmethod
    # ------------------------------------------------------------------------------------------ #
    def select_by_day(day):
        day_plus_one = day + datetime.timedelta(days=1)
        tracks = DB.session.query(TrackService).filter(TrackService.start >= str(day), TrackService.start < str(day_plus_one)).all()
        return tracks

317
318
319
320
321
322
323
    @staticmethod
    # ------------------------------------------------------------------------------------------ #
    def select_by_range(from_day, to_day):
        tracks = DB.session.query(TrackService).filter(TrackService.start >= str(from_day),
                                                       TrackService.start < str(to_day)).all()
        return tracks

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
324
325
326
327
    # ------------------------------------------------------------------------------------------ #
    def __str__(self):
        return "TrackServiceID: #" + str(self.trackservice_id) + " playlist_id: " + str(self.playlist_id) + " started @ " + str(self.start) + " and played " + self.source

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
# ------------------------------------------------------------------------------------------ #
# class TrackServiceSchedule(db.Model, AuraDatabaseModel):
#     """
#     Trackservice is tracking every schedule.
#     """
#     __tablename__ = 'trackservice_schedule'
#
#     # primary and foreign keys
#     ts_schedule_id = Column(Integer, primary_key=True, autoincrement=True)
#     schedule_id = Column(Integer, ForeignKey("schedule.schedule_id"))
#
#     schedule = relationship("Schedule", foreign_keys=[schedule_id], lazy="joined")
#
#     # ------------------------------------------------------------------------------------------ #
#     @staticmethod
#     def select_one(schedule_id):
#         # damn BAND-AID
#         # db.session.commit()
#
#         return db.session.query(ScheduleEntry).filter(TrackServiceSchedule.schedule_id == schedule_id).first()
#
# # ------------------------------------------------------------------------------------------ #
# class TrackServiceScheduleEntry(db.Model, AuraDatabaseModel):
#     """
#     And a schedule can have multiple entries
#     """
#     __tablename__ = 'trackservice_entry'
#
#     # primary and foreign keys. the foreign keys here can be null, because of fallback stuff
#     ts_entry_id = Column(Integer, primary_key=True, autoincrement=True)
#     ts_schedule_id = Column(Integer, ForeignKey("trackservice_schedule.ts_schedule_id"), nullable=True)
#     playlist_id = Column(Integer, nullable=True)
#     entry_num = Column(Integer, nullable=True)
#
#     fallback = Column(Boolean, default=False)
#     fallback_start = Column(DateTime, nullable=True, default=None)
#     source = Column(String(256), nullable=True, default=None)
#
#     # foreign key definitions
#     __table_args__ = (
#         ForeignKeyConstraint(['playlist_id', 'entry_num'], ['schedule_entry.playlist_id', 'schedule_entry.entry_num']),
#     )
#
#     trackservice_schedule = relationship("TrackServiceSchedule", foreign_keys=[ts_schedule_id], lazy="joined")
#     #trackservice_entry = relationship("ScheduleEntry", foreign_keys=[playlist_id, entry_num], lazy="joined")
#     trackservice_entry = relationship("ScheduleEntry", primaryjoin="and_(TrackServiceScheduleEntry.playlist_id==ScheduleEntry.playlist_id, TrackServiceScheduleEntry.entry_num==ScheduleEntry.entry_num)" , lazy="joined")
#
#     @staticmethod
#     def select_all():
#         return db.session.query(TrackServiceScheduleEntry).filter().all()

379
#AuraDatabaseModel.recreate_db(systemexit=True)