From 9046b6612edd319eaa9187eabfb09e4aa2639c9b Mon Sep 17 00:00:00 2001 From: David Trattnig <david.trattnig@o94.at> Date: Tue, 8 Sep 2020 18:40:47 +0200 Subject: [PATCH] Update/delete playlist entries. #31 --- modules/base/models.py | 680 ++------------------------------ modules/plugins/trackservice.py | 2 +- modules/scheduling/calendar.py | 49 ++- 3 files changed, 67 insertions(+), 664 deletions(-) diff --git a/modules/base/models.py b/modules/base/models.py index 528c7a4d..e062cf85 100644 --- a/modules/base/models.py +++ b/modules/base/models.py @@ -88,6 +88,14 @@ class AuraDatabaseModel(): DB.session.commit() + def refresh(self): + """ + Refreshes the currect record + """ + DB.session.expire(self) + DB.session.refresh(self) + + def _asdict(self): return self.__dict__ @@ -301,7 +309,7 @@ class Playlist(DB.Model, AuraDatabaseModel): @staticmethod - def select_playlist_for_schedule(datetime, playlist_id): + def select_playlist_for_schedule(start_date, playlist_id): """ Retrieves the playlist for the given schedule identified by `start_date` and `playlist_id` @@ -316,18 +324,14 @@ class Playlist(DB.Model, AuraDatabaseModel): Exception: In case there a inconsistent database state, such es multiple playlists for given date/time. """ playlist = None - playlists = DB.session.query(Playlist).filter(Playlist.schedule_start == datetime).all() + playlists = DB.session.query(Playlist).filter(Playlist.schedule_start == start_date).all() # FIXME There are unknown issues with the native SQL query by ID # playlists = DB.session.query(Playlist).filter(Playlist.schedule_start == datetime and Playlist.playlist_id == playlist_id).all() for p in playlists: if p.playlist_id == playlist_id: playlist = p - # if playlists and len(playlists) > 1: - # raise Exception("Inconsistent Database State: Multiple playlists for given schedule '%s' and playlist id#%d available!" % (str(datetime), playlist_id)) - # if not playlists: - # return None - # return playlists[0] + return playlist @@ -446,8 +450,30 @@ class PlaylistEntry(DB.Model, AuraDatabaseModel): @staticmethod def select_playlistentry_for_playlist(artificial_playlist_id, entry_num): + """ + Selects one entry identified by `playlist_id` and `entry_num`. + """ return DB.session.query(PlaylistEntry).filter(PlaylistEntry.artificial_playlist_id == artificial_playlist_id, PlaylistEntry.entry_num == entry_num).first() + @staticmethod + def delete_entry(artificial_playlist_id, entry_num): + """ + Deletes the playlist entry and associated metadata. + """ + entry = PlaylistEntry.select_playlistentry_for_playlist(artificial_playlist_id, entry_num) + metadata = PlaylistEntryMetaData.select_metadata_for_entry(entry.artificial_id) + metadata.delete() + entry.delete() + DB.session.commit() + + @staticmethod + def count_entries(artificial_playlist_id): + """ + Returns the count of all entries. + """ + result = DB.session.query(PlaylistEntry).filter(PlaylistEntry.artificial_playlist_id == artificial_playlist_id).count() + return result + @hybrid_property def entry_end(self): return self.entry_start + datetime.timedelta(seconds=self.duration) @@ -547,644 +573,6 @@ class PlaylistEntryMetaData(DB.Model, AuraDatabaseModel): @staticmethod def select_metadata_for_entry(artificial_playlistentry_id): - return DB.session.query(PlaylistEntry).filter(PlaylistEntryMetaData.artificial_entry_id == artificial_playlistentry_id).first() - - - -# -# TRACK SERVICE -# - - -# class TrackService(DB.Model, AuraDatabaseModel): -# """ -# TrackService holding track-service items consisting of -# """ -# __tablename__ = 'trackservice' - -# # Primary keys -# id = Column(Integer, primary_key=True, autoincrement=True) - -# # Foreign keys -# track_start = Column(DateTime) -# track_end = Column(DateTime) # Currently not used, maybe later for timing checks and multi-entry avoidance. -# artificial_schedule_id = Column(Integer, ForeignKey("schedule.id")) -# artificial_playlist_entry_id = Column(Integer, ForeignKey("playlist_entry.artificial_id"), nullable=True) -# single_entry_id = Column(Integer, ForeignKey("single_entry.id"), nullable=True) - -# # Data -# schedule = relationship("Schedule", foreign_keys=[artificial_schedule_id], lazy="joined") -# playlist_entry = relationship("PlaylistEntry", primaryjoin="and_(TrackService.artificial_playlist_entry_id==PlaylistEntry.artificial_id)", lazy="joined") -# single_entry = relationship("SingleEntry", foreign_keys=[single_entry_id], lazy="joined") - -# fallback_type = Column(Integer, default=0) - - -# def __init__(self, entry, fallback_type=0): -# """ -# Initializes a trackservice entry based on a playlist entry. -# """ -# self.track_start = datetime.datetime.now() -# # if entry.duration: -# # self.track_end = self.track_start + datetime.timedelta(seconds=entry.duration) -# self.fallback_type = fallback_type - -# if fallback_type < 4: -# self.schedule_start = entry.playlist.schedule_start -# self.artificial_playlist_entry_id = entry.artificial_id -# self.playlist_entry = entry -# self.schedule = entry.playlist.schedule -# else: -# self.single_entry = entry - - -# @hybrid_property -# def track(self): -# """ -# Retrieves the track information as a dictionary. - -# Depending on possible fallback scenarios either `playlist_entry` or `single_entry` is used as a basis: - -# - Scenario 1: No fallback, all info is gathered via the playlist entry -# - Scenario 2: Fallback-type > 0, info is also gathered via the defined playlist entry -# - Scenario 3: This type of fallback didn't get scheduled; a single entry is played -# """ -# if self.playlist_entry: -# return self.playlist_entry.as_dict() -# elif self.single_entry: -# return self.single_entry.as_dict() -# else: -# return None - - -# @hybrid_property -# def show(self): -# """ -# Retrieves show information based on the related schedule. If no schedule -# is available (e.g. when the engine is in a fallback state), then the default -# show properties from `AuraConfig` are returned. -# """ -# show_info = {} - -# if self.schedule: -# show_info["name"] = self.schedule.show_name -# show_info["type"] = self.schedule.type -# show_info["host"] = self.schedule.show_hosts -# elif self.fallback_type == 4: -# show_info["name"] = config.get("fallback_show_name") -# show_info["type"] = config.get("fallback_show_type") -# show_info["host"] = config.get("fallback_show_host") - -# return show_info - - -# @staticmethod -# def select_one(id): -# """ -# Select one specific track-service item by ID. -# """ -# DB.session.commit() # Required since independend session is used. -# track = DB.session.query(TrackService).filter(TrackService.id == id).first() -# return track - - -# @staticmethod -# def select_current(): -# """ -# Selects the currently playing track. -# """ -# now = datetime.datetime.now() -# DB.session.commit() # Required since independend session is used. -# track = DB.session.query(TrackService).\ -# filter(TrackService.track_start <= str(now)).\ -# order_by(TrackService.track_start.desc()).first() -# return track - - -# @staticmethod -# def select_last_hours(n): -# """ -# Selects the tracks playing in the past (`n`) hours. -# """ -# last_hours = datetime.datetime.today() - datetime.timedelta(hours=n) -# DB.session.commit() # Required since independend session is used. -# tracks = DB.session.query(TrackService).filter(TrackService.track_start >= str(last_hours)).all() -# for track in tracks: -# track = TrackService.select_one(track.id) -# return tracks - - -# @staticmethod -# def select_by_day(day): -# """ -# Select the track-service items for a day. -# """ -# day_plus_one = day + datetime.timedelta(days=1) -# DB.session.commit() # Required since independend session is used. -# tracks = DB.session.query(TrackService).\ -# filter(TrackService.track_start >= str(day), TrackService.track_start < str(day_plus_one)).\ -# order_by(TrackService.track_start.desc()).all() - -# res = [] -# for item in tracks: -# if item.track: res.append(item) -# return res - - -# @staticmethod -# def select_by_range(from_day, to_day): -# """ -# Selects the track-service items for a day range. -# """ -# DB.session.commit() -# tracks = DB.session.query(TrackService).filter(TrackService.track_start >= str(from_day), -# TrackService.track_start < str(to_day)).all() -# return tracks - - -# def __str__(self): -# """ -# Convert to String. -# """ -# return "TrackID: #%s [track_start: %s, artificial_playlist_entry_id: %s]" % (str(self.id), str(self.track_start), str(self.artificial_playlist_entry_id)) - - - -# class SingleEntry(DB.Model, AuraDatabaseModel): -# """ -# An entry played in case of e.g. a local fallback or custom programming without a playlist nor schedule. -# """ -# __tablename__ = 'single_entry' - -# # Primary keys -# id = Column(Integer, primary_key=True) - -# # Relationships -# trackservice_id = Column(Integer) #, ForeignKey("trackservice.id")) -# meta_data_id = Column(Integer) #, ForeignKey("trackservice.id")) - -# trackservice = relationship("TrackService", uselist=False, back_populates="single_entry") -# meta_data = relationship("SingleEntryMetaData", uselist=False, back_populates="entry") - -# # Data -# uri = Column(String(1024)) -# duration = Column(BigInteger) -# source = Column(String(1024)) -# entry_start = Column(DateTime) - -# queue_state = None # Assigned when entry is about to be queued -# channel = None # Assigned when entry is actually played -# status = None # Assigned when state changes - - -# @hybrid_property -# def entry_end(self): -# return self.entry_start + datetime.timedelta(seconds=self.duration) - -# @hybrid_property -# def start_unix(self): -# return time.mktime(self.entry_start.timetuple()) - -# @hybrid_property -# def end_unix(self): -# return time.mktime(self.entry_start.timetuple()) + self.duration - -# @hybrid_property -# def volume(self): -# return 100 - -# @hybrid_property -# def type(self): -# return EngineUtil.get_channel_type(self.uri) - - -# def as_dict(self): -# """ -# Returns the entry as a dictionary for serialization. -# """ -# if self.meta_data: -# return { -# "duration": self.duration, -# "artist": self.meta_data.artist, -# "album": self.meta_data.album, -# "title": self.meta_data.title -# } -# return None - - -# def __str__(self): -# """ -# String representation of the object. -# """ -# time_start = SimpleUtil.fmt_time(self.start_unix) -# time_end = SimpleUtil.fmt_time(self.end_unix) -# track = self.source[-25:] -# return "SingleEntry #%s [%s - %s | %ssec | Source: ...%s]" % (str(self.id), time_start, time_end, self.duration, track) - + return DB.session.query(PlaylistEntryMetaData).filter(PlaylistEntryMetaData.artificial_entry_id == artificial_playlistentry_id).first() -# class SingleEntryMetaData(DB.Model, AuraDatabaseModel): -# """ -# Metadata for a autonomous entry such as the artist and track name. -# """ -# __tablename__ = "single_entry_metadata" - -# id = Column(Integer, primary_key=True) -# single_entry_id = Column(Integer, ForeignKey("single_entry.id")) - -# artist = Column(String(256)) -# title = Column(String(256)) -# album = Column(String(256)) - -# entry = relationship("SingleEntry", uselist=False, back_populates="meta_data") - -# @staticmethod -# def select_metadata_for_entry(single_entry_id): -# return DB.session.query(SingleEntry).filter(SingleEntryMetaData.id == single_entry_id).first() - - - - - -# -# LEGACY CLASSES -# - - - -# ------------------------------------------------------------------------------------------ # -# class Schedule(DB.Model, AuraDatabaseModel): -# """ -# One specific Schedule for a show on a timeslot -# """ -# __tablename__ = 'schedule' -# -# # primary and foreign keys -# schedule_start = Column(DateTime, primary_key=True) -# -# schedule_end = Column(DateTime) -# schedule_id = Column(Integer) #, primary_key=True, autoincrement=False) -# show_id = Column(Integer) # well, in fact not needed.. -# show_name = Column(String(256)) -# show_hosts = Column(String(256)) -# funding_category = Column(String(256)) -# comment = Column(String(512)) -# languages = Column(String(256)) -# type = Column(String(256)) -# category = Column(String(256)) -# topic = Column(String(256)) -# musicfocus = Column(String(256)) -# -# is_repetition = Column(Boolean()) -# -# playlist_id = Column(Integer, ForeignKey("playlist.playlist_id")) -# timeslot_fallback_id = Column(Integer) -# show_fallback_id = Column(Integer) -# station_fallback_id = Column(Integer) -# -# playlist = relationship("Playlist", foreign_keys=[playlist_id], lazy="joined") -# # timeslot_fallback = relationship("Playlist", foreign_keys=[timeslot_fallback_id], lazy="joined") -# # show_fallback = relationship("Playlist", foreign_keys=[show_fallback_id], lazy="joined") -# # station_fallback = relationship("Playlist", foreign_keys=[station_fallback_id], lazy="joined") -# -# @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 -# @staticmethod -# def select_act_programme(): -# #DB.session.query(Schedule).filter -# # fetching all from today to .. -# today = datetime.date.today() -# all_entries = DB.session.query(Schedule).filter(Schedule.schedule_start >= today).order_by(Schedule.schedule_start).all() -# -# return all_entries -# - -# -# @staticmethod -# def drop_the_future(timedelta): -# then = datetime.datetime.now() + timedelta -# -# # is this really necessary? -# future_entries = DB.session.query(Schedule).filter(Schedule.schedule_start > then) -# for e in future_entries: -# e.delete() -# DB.session.commit() -# -# 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 -# -# # ------------------------------------------------------------------------------------------ # -# def __str__(self): -# return "ScheduleID: #" + str(self.schedule_id) + " Showname: " + self.show_name + " starts @ " + str(self.schedule_start) - -# ------------------------------------------------------------------------------------------ # - - - -# ------------------------------------------------------------------------------------------ # -#class PlaylistEntry(DB.Model, AuraDatabaseModel): - # __tablename__ = 'playlist_entry' - # - # # primary and foreign keys - # playlist_id = Column(Integer, ForeignKey("playlist.playlist_id"), primary_key=True, nullable=False, autoincrement=True) - # entry_num = Column(Integer, primary_key=True, nullable=False, autoincrement=False) - # - # uri = Column(String(1024)) - # - # source = "" - # cleansource = "" - # cleanprotocol = "" - # type = None - # fadeintimer = None - # fadeouttimer = None - # switchtimer = None - # - # meta_data = relationship("PlaylistEntryMetaData", primaryjoin="and_(PlaylistEntry.playlist_id==PlaylistEntryMetaData.playlist_id, PlaylistEntry.entry_num==PlaylistEntryMetaData.entry_num)", lazy="joined") - # - # # normal constructor - # def __init__(self, **kwargs): - # super(PlaylistEntry, self).__init__(**kwargs) - # self.calc_unix_times() - # self.define_clean_source() - # - # # constructor like - called from sqlalchemy - # @orm.reconstructor - # def reconstructor(self): - # self.calc_unix_times() - # self.define_clean_source() - # self.set_entry_type() - # - # def define_clean_source(self): - # if self.uri is None: - # return None - # - # if self.uri.startswith("http"): - # self.cleanprotocol = self.uri[:7] - # self.cleansource = self.uri - # - # elif self.uri.startswith("linein"): - # self.cleanprotocol = self.uri[:9] - # self.cleansource = self.uri[9:] - # - # elif self.uri.startswith("pool") or self.uri.startswith("file") or self.uri.startswith("live"): - # self.cleanprotocol = self.uri[:7] - # self.cleansource = self.uri[7:] - # - # elif self.uri.startswith("playlist"): - # self.cleanprotocol = self.uri[:11] - # self.cleansource = self.uri[11:] - # - # else: - # self.logger.error("Unknown source protocol") - # - # def set_entry_type(self): - # if self.uri.startswith("http"): - # self.type = ScheduleEntryType.HTTP - # if self.uri.startswith("pool") or self.uri.startswith("playlist") or self.uri.startswith("file"): - # self.type = ScheduleEntryType.FILESYSTEM - # if self.uri.startswith("live") or self.uri.startswith("linein"): - # 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 - - - - - # def calc_unix_times(self): - # if self.entry_start is not None: - # self.entry_start_unix = time.mktime(self.entry_start.timetuple()) - # - # - # - # # ------------------------------------------------------------------------------------------ # - # @staticmethod - # def select_all(): - # # fetching all entries - # all_entries = DB.session.query(Playlist).filter(Playlist.fallback_type == 0).order_by(Playlist.entry_start).all() - # - # cnt = 0 - # for entry in all_entries: - # entry.programme_index = cnt - # cnt = cnt + 1 - # - # return all_entries - # - # @staticmethod - # def select_act_programme(include_act_playing = True): - # # fetching all from today to .. - # today = datetime.date.today() - # all_entries = DB.session.query(Playlist).filter(Playlist.entry_start >= today, Playlist.fallback_type == 0).order_by(Playlist.entry_start).all() - # - # cnt = 0 - # for entry in all_entries: - # entry.programme_index = cnt - # cnt = cnt + 1 - # - # return all_entries - # - # # ------------------------------------------------------------------------------------------ # - # @staticmethod - # def truncate(): - # all_entries = DB.session.query(Playlist).filter().order_by(Playlist.entry_start).all() - # - # for a in all_entries: - # a.delete() - # DB.session.commit() - # - # # ------------------------------------------------------------------------------------------ # - # @staticmethod - # def select_next_manual_entry_num(): - # - # max_manual_entry_num = DB.session.query(func.max(Playlist.entry_num)).filter(Playlist.schedule_id == 0).first() - # - # if max_manual_entry_num[0] is None: - # return 0 - # else: - # return int(max_manual_entry_num[0])+1 - # - # # ------------------------------------------------------------------------------------------ # - # @staticmethod - # def select_upcoming(datefrom=datetime.datetime.now()): - # upcomingtracks = DB.session.query(Playlist).filter(Playlist.entry_start > datefrom).order_by(Playlist.entry_start).all() - # return upcomingtracks - # - # # ------------------------------------------------------------------------------------------ # - # @staticmethod - # def select_one(playlist_id, entry_num): - # return DB.session.query(Playlist).filter(Playlist.playlist_id == playlist_id, Playlist.entry_num == entry_num).first() - # - # # ------------------------------------------------------------------------------------------ # - # @staticmethod - # def select_one_playlist_entry_for_show(schedule_id, playlist_type, entry_num): - # return DB.session.query(Playlist).filter(Playlist.schedule_id == schedule_id, Playlist.fallback_type == playlist_type, Playlist.entry_num == entry_num).first() - # - # # ------------------------------------------------------------------------------------------ # - # @staticmethod - # def select_playlist(playlist_id): - # return DB.session.query(Playlist).filter(Playlist.playlist_id == playlist_id).order_by(Playlist.entry_start).all() - # - # @staticmethod - # def drop_the_future(timedelta): - # then = datetime.datetime.now() + timedelta - # #DB.session.delete(ScheduleEntry).filter(ScheduleEntry.entry_start >= then) - # - # # is this really necessary? - # future_entries = DB.session.query(Playlist).filter(Playlist.entry_start > then) - # for e in future_entries: - # e.delete() - # DB.session.commit() - # - # def getChannel(self): - # if self.type == self.type.FILESYSTEM: - # return "fs" - # - # 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: - # return "aura_linein_"+self.cleansource # .cleanprotocol[8] - # - # if self.type == self.type.HTTP: - # return "http" - # - # - # # ------------------------------------------------------------------------------------------ # - # def __str__(self): - # return "Showentry starts @ " + str(self.entry_start) + " and plays " + self.source - - -# class ScheduleEntryFile(DB.Model, AuraDatabaseModel): -# __tablename__ = 'schedule_entry_file' -# -# # primary and foreign keys -# file_id = Column(Integer, primary_key=True, nullable=False, autoincrement=False) -# playlist_id = Column(Integer) #, ForeignKey("schedule_entry.playlist_id")) # primary_key=True, nullable=False, autoincrement=False) -# entry_num = Column(Integer) # , ForeignKey("schedule_entry.entry_num")) # primary_key=True, nullable=False, autoincrement=False) -# -# ForeignKeyConstraint(["playlist_id", "entry_num"], ["schedule_entry.playlist_id", "schedule_entry.entry_num"]) -# -# show = Column(String(512)) -# size = Column(Integer) -# duration = Column(Integer) -# -# class ScheduleEntryFileMetaData(DB.Model, AuraDatabaseModel): -# __tablename__ = "schedule_entry_file_metadata" -# -# metadata_id = Column(Integer, primary_key=True, nullable=False, autoincrement=True) -# file_id = Column(Integer, ForeignKey("schedule_entry_file.file_id")) -# -# artist = Column(String(256)) -# title = Column(String(256)) -# album = Column(String(256)) -# -# # ------------------------------------------------------------------------------------------ # -# class TrackService(DB.Model, AuraDatabaseModel): -# __tablename__ = 'trackservice' -# -# trackservice_id = Column(Integer, primary_key=True, autoincrement=True) -# schedule_entry_id = Column(Integer, ForeignKey("schedule_entry.id")) -# playlist_id = Column(Integer, nullable=False) -# entry_num = Column(Integer, nullable=False) -# -# source = Column(String(255), nullable=False) -# start = Column(DateTime, nullable=False, default=func.now()) - -# __table_args__ = ( -# ForeignKeyConstraint(['playlist_id', 'entry_num'], ['schedule_entry.playlist_id', 'schedule_entry.entry_num']), -# ) -# schedule_entry = relationship("ScheduleEntry", primaryjoin="and_(TrackService.playlist_id==ScheduleEntry.playlist_id, TrackService.entry_num==ScheduleEntry.entry_num)", lazy="joined") - - #schedule = relationship("Schedule", foreign_keys=[schedule_id], lazy="joined") - # trackservice_entry = relationship("ScheduleEntry", foreign_keys=[playlist_id, entry_num], lazy="joined") - # schedule_entry = relationship("ScheduleEntry", primaryjoin="and_(TrackService.schedule_entry_id==ScheduleEntry.id)", lazy="joined") - # - # @staticmethod - # # ------------------------------------------------------------------------------------------ # - # def select_one(trackservice_id): - # return DB.session.query(TrackService).filter(TrackService.trackservice_id == trackservice_id).first() - # - # @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 - # - # @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 - # - # # ------------------------------------------------------------------------------------------ # - # def __str__(self): - # return "TrackServiceID: #" + str(self.trackservice_id) + " playlist_id: " + str(self.playlist_id) + " started @ " + str(self.start) + " and played " + self.source - -# ------------------------------------------------------------------------------------------ # -# 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() - -# AuraDatabaseModel.recreate_db(systemexit=True) diff --git a/modules/plugins/trackservice.py b/modules/plugins/trackservice.py index 5cefc667..f12d3154 100644 --- a/modules/plugins/trackservice.py +++ b/modules/plugins/trackservice.py @@ -85,7 +85,7 @@ class TrackServiceHandler(): """ Posts the current and next show information to the Engine API. """ - current_playlist = self.soundsystem.scheduler.get_active_playlist() + current_playlist = self.soundsystem.scheduler.get_active_playlist() current_schedule = current_playlist.schedule next_schedule = self.soundsystem.scheduler.get_next_schedules(1) if next_schedule: next_schedule = next_schedule[0] diff --git a/modules/scheduling/calendar.py b/modules/scheduling/calendar.py index 9571b7e3..f920fa55 100644 --- a/modules/scheduling/calendar.py +++ b/modules/scheduling/calendar.py @@ -124,8 +124,6 @@ class AuraCalendarService(threading.Thread): msg = "Schedule #%s has been deleted remotely. Since the scheduling window has already started, it won't be deleted locally." % local_schedule.schedule_id self.logger.warn(SimpleUtil.red(msg)) - - # Process fetched schedules for schedule in fetched_schedule_data: @@ -149,8 +147,6 @@ class AuraCalendarService(threading.Thread): if schedule_db.station_fallback_id: self.store_playlist(schedule_db, schedule_db.station_fallback_id, schedule["station_fallback"], PlaylistType.STATION.id) - - result.append(schedule_db) # Release the mutex @@ -195,10 +191,6 @@ class AuraCalendarService(threading.Thread): schedule_db.topic = schedule["show_topics"] schedule_db.musicfocus = schedule["show_musicfocus"] - # if schedule["playlist_id"] is None: - # # FIXME Manually assigned playlist ID. - # schedule["playlist_id"] = 1 - schedule_db.playlist_id = schedule["playlist_id"] schedule_db.schedule_fallback_id = schedule["schedule_fallback_id"] schedule_db.show_fallback_id = schedule["show_fallback_id"] @@ -236,7 +228,7 @@ class AuraCalendarService(threading.Thread): playlist_db.entry_count = 0 playlist_db.store(havetoadd, commit=True) - + if playlist_db.entry_count > 0: self.store_playlist_entries(schedule_db, playlist_db, fetched_playlist) @@ -251,6 +243,10 @@ class AuraCalendarService(threading.Thread): entry_num = 0 time_marker = playlist_db.start_unix + # If existing playlist entries are beyond the count of the new playlist entries, + # then delete those first + self.delete_orphaned_entries(playlist_db, fetched_playlist) + for entry in fetched_playlist["entries"]: entry_db = PlaylistEntry.select_playlistentry_for_playlist(playlist_db.artificial_id, entry_num) havetoadd = False @@ -289,6 +285,25 @@ class AuraCalendarService(threading.Thread): + def delete_orphaned_entries(self, playlist_db, fetched_playlist): + """ + Deletes all playlist entries which are beyond the current playlist's `entry_count`. + Such entries might be existing due to a remotely changed playlist, which now has + less entries than before. + """ + new_last_idx = len(fetched_playlist["entries"]) + existing_last_idx = PlaylistEntry.count_entries(playlist_db.artificial_id)-1 + + if existing_last_idx < new_last_idx: + return + + for entry_num in range(new_last_idx, existing_last_idx+1, 1): + PlaylistEntry.delete_entry(playlist_db.artificial_id, entry_num) + self.logger.info(SimpleUtil.yellow("Deleted playlist entry %s:%s" % (playlist_db.artificial_id, entry_num))) + entry_num += 1 + + + def store_playlist_entry_metadata(self, entry_db, metadata): """ Stores the meta-data for a PlaylistEntry. @@ -301,20 +316,20 @@ class AuraCalendarService(threading.Thread): metadata_db.artificial_entry_id = entry_db.artificial_id - if "artist" not in metadata: - self.logger.warning("Artist not found in metadata for track '%s'. Setting to ''" % entry_db.source) - metadata_db.artist = "" - else: + if "artist" in metadata: metadata_db.artist = metadata["artist"] + else: + metadata_db.artist = "" if "album" in metadata: metadata_db.album = metadata["album"] + else: + metadata_db.album = "" - if "title" not in metadata: - self.logger.warning("Title not found in metadata for track '%s'. Setting to 'n/a'" % entry_db.source) - metadata_db.title = "" + if "title" in metadata: + metadata_db.title = metadata["title"] else: - metadata_db.artist = metadata["title"] + metadata_db.title = "" metadata_db.store(havetoadd, commit=True) -- GitLab