broadcasts.py 14.3 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
164
165
    def reconstructor(self):
        self.calc_unix_times()
        self.set_entry_type()

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

        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]
177
            self.cleansource = self.source[7:]
178
179
180

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

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

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

190
191
    def set_entry_type(self):
        if self.source.startswith("http"):
192
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"):
            self.type = ScheduleEntryType.LIVE
197

198
199
200
    # ------------------------------------------------------------------------------------------ #
    @staticmethod
    def select_all():
201
        # fetching all entries
202
        all_entries = DB.session.query(ScheduleEntry).filter(ScheduleEntry.fallback_type == 0).order_by(ScheduleEntry.entry_start).all()
203
204
205
206
207
208
209
210

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

        return all_entries

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
    @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

226
227
228
229
230
231
232
233
234
235
    # ------------------------------------------------------------------------------------------ #
    @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()

    # ------------------------------------------------------------------------------------------ #
236
237
238
    @staticmethod
    def select_next_manual_entry_num():

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
239
        max_manual_entry_num = DB.session.query(func.max(ScheduleEntry.entry_num)).filter(ScheduleEntry.schedule_id == 0).first()
240
241
242
243
244
245
246
247
248

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

    # ------------------------------------------------------------------------------------------ #
    @staticmethod
    def upcoming(datefrom=datetime.datetime.now()):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
249
        upcomingtracks = DB.session.query(ScheduleEntry).filter(ScheduleEntry.start > datefrom).all()
250
251
252
253
254
        return upcomingtracks

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

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
257
258
259
260
261
    # ------------------------------------------------------------------------------------------ #
    @staticmethod
    def select_playlist(playlist_id):
        return DB.session.query(ScheduleEntry).filter(ScheduleEntry.playlist_id == playlist_id).all()

262
263
264
265
266
267
268
269
270
271
272
    def getChannel(self):
        if self.type == self.type.FILESYSTEM:
            return "fs"

        if self.type == self.type.LIVE:
            return "aura_linein_"+self.cleansource # .cleanprotocol[8]

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


273
274
    # ------------------------------------------------------------------------------------------ #
    def __str__(self):
275
        return "ScheduleID: #" + str(self.schedule_id) + " Showname: " + self.schedule.show_name + " starts @ " + str(self.entry_start) + " and plays " + self.source
276

277

278
# ------------------------------------------------------------------------------------------ #
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
279
class TrackService(DB.Model, AuraDatabaseModel):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
280
    __tablename__ = 'trackservice'
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
281

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
282
283
284
    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
285

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
286
    source = Column(String(255), nullable=False)
287
    start = Column(DateTime, nullable=False, default=func.now())
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
288

289
290
291
    __table_args__ = (
        ForeignKeyConstraint(['playlist_id', 'entry_num'], ['schedule_entry.playlist_id', 'schedule_entry.entry_num']),
    )
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
292

293
    #schedule = relationship("Schedule", foreign_keys=[schedule_id], lazy="joined")
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
294
295
    # 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")
296

297
    @staticmethod
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
298
    # ------------------------------------------------------------------------------------------ #
299
    def select_one(trackservice_id):
Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
300
        return DB.session.query(TrackService).filter(TrackService.trackservice_id == trackservice_id).first()
301

Gottfried Gaisbauer's avatar
Gottfried Gaisbauer committed
302
303
304
305
306
307
308
    @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

309
310
311
312
313
314
315
    @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
316
317
318
319
    # ------------------------------------------------------------------------------------------ #
    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
320
321
322
323
324
325
326
327
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
# ------------------------------------------------------------------------------------------ #
# 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()

371
#AuraDatabaseModel.recreate_db(systemexit=True)