]> git.0d.be Git - botaradio.git/commitdiff
REFACTOR: DIFFERENT PLAYLIST #91
authorTerry Geng <gengyanda@gmail.com>
Thu, 5 Mar 2020 17:39:08 +0000 (01:39 +0800)
committerTerry Geng <gengyanda@gmail.com>
Thu, 5 Mar 2020 17:39:24 +0000 (01:39 +0800)
command.py
configuration.default.ini
interface.py
media/playlist.py
media/radio.py
media/url.py
mumbleBot.py
templates/playlist.html

index 2a46905416be399e25405bee51f80b5f0b015dff..cf84e41056ca0bfede92c019aa5000a3a8758493 100644 (file)
@@ -137,13 +137,13 @@ def cmd_url_unban(bot, user, text, command, parameter):
 def cmd_play(bot, user, text, command, parameter):
     global log
 
-    if var.playlist.length() > 0:
+    if len(var.playlist) > 0:
         if parameter:
             if parameter.isdigit() and 1 <= int(parameter) <= len(var.playlist):
                 var.playlist.point_to(int(parameter) - 1 - 1) # First "-1" transfer 12345 to 01234, second "-1"
                                                             # point to the previous item. the loop will next to
                                                             # the one you want
-                bot.interrupt_playing()
+                bot.interrupt()
             else:
                 bot.send_msg(constants.strings('invalid_index', index=parameter), text)
 
@@ -283,7 +283,7 @@ def cmd_play_url(bot, user, text, command, parameter):
 
     log.info("cmd: add to playlist: " + music_wrapper.format_debug_string())
     bot.send_msg(constants.strings('file_added', item=music_wrapper.format_song_string()), text)
-    if var.playlist.length() == 2:
+    if len(var.playlist) == 2:
         # If I am the second item on the playlist. (I am the next one!)
         bot.async_download_next()
 
@@ -617,7 +617,7 @@ def cmd_current_music(bot, user, text, command, parameter):
     global log
 
     reply = ""
-    if var.playlist.length() > 0:
+    if len(var.playlist) > 0:
         bot.send_msg(var.playlist.current_item().format_current_playing())
     else:
         reply = constants.strings('not_playing')
@@ -627,9 +627,9 @@ def cmd_current_music(bot, user, text, command, parameter):
 def cmd_skip(bot, user, text, command, parameter):
     global log
 
-    if var.playlist.length() > 0:
-        bot.interrupt_playing()
-    else:
+    bot.interrupt()
+
+    if len(var.playlist) == 0:
         bot.send_msg(constants.strings('queue_empty'), text)
 
 
@@ -637,7 +637,7 @@ def cmd_last(bot, user, text, command, parameter):
     global log
 
     if len(var.playlist) > 0:
-        bot.interrupt_playing()
+        bot.interrupt()
         var.playlist.point_to(len(var.playlist) - 1)
     else:
         bot.send_msg(constants.strings('queue_empty'), text)
@@ -648,7 +648,7 @@ def cmd_remove(bot, user, text, command, parameter):
 
     # Allow to remove specific music into the queue with a number
     if parameter and parameter.isdigit() and int(parameter) > 0 \
-            and int(parameter) <= var.playlist.length():
+            and int(parameter) <= len(var.playlist):
 
         index = int(parameter) - 1
 
@@ -658,14 +658,14 @@ def cmd_remove(bot, user, text, command, parameter):
 
             if index < len(var.playlist):
                 if not bot.is_pause:
-                    bot.interrupt_playing()
+                    bot.interrupt()
                     var.playlist.current_index -= 1
                     # then the bot will move to next item
 
             else: # if item deleted is the last item of the queue
                 var.playlist.current_index -= 1
                 if not bot.is_pause:
-                    bot.interrupt_playing()
+                    bot.interrupt()
         else:
             removed = var.playlist.remove(index)
 
@@ -730,7 +730,7 @@ def cmd_queue(bot, user, text, command, parameter):
 def cmd_random(bot, user, text, command, parameter):
     global log
 
-    bot.interrupt_playing()
+    bot.interrupt()
     var.playlist.randomize()
 
 def cmd_repeat(bot, user, text, command, parameter):
@@ -760,14 +760,13 @@ def cmd_mode(bot, user, text, command, parameter):
         bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
     else:
         var.db.set('playlist', 'playback_mode', parameter)
-        var.playlist.set_mode(parameter)
+        var.playlist = media.playlist.get_playlist(parameter, var.playlist)
         log.info("command: playback mode changed to %s." % parameter)
         bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
                                        user=bot.mumble.users[text.actor]['name']), text)
         if parameter == "random":
-            bot.stop()
-            var.playlist.randomize()
-            bot.launch_music(0)
+            bot.interrupt()
+            bot.launch_music()
 
 def cmd_drop_database(bot, user, text, command, parameter):
     global log
index 0ea29da6efc8201a0791438a7510b1fdf47591e6..f642005c36990dcad3d77c36b8e4b0c4c89dda2a 100644 (file)
@@ -193,7 +193,7 @@ url_from_playlist_item = <a href="{url}">{title}</a> <i>from playlist</i> <a hre
 url_item = <a href="{url}">{title}</a> <i>added by</i> {user}
 not_in_my_channel = You're not in my channel, command refused!
 pm_not_allowed = Private message aren't allowed.
-too_long = This music is too long, skip!
+too_long = {song} is too long, removed from playlist!
 download_in_progress = Download of {item} in progress...
 removing_item = Removed entry {item} from playlist.
 user_ban = You are banned, not allowed to do that!
index 18a64f3146b957021df28878a73be87d40947947..2aaa31fc113d7e5044955518752d87c2baf7fa4f 100644 (file)
@@ -120,7 +120,7 @@ def index():
 @web.route("/playlist", methods=['GET'])
 @requires_auth
 def playlist():
-    if var.playlist.length() == 0:
+    if len(var.playlist) == 0:
         return jsonify({'items': [render_template('playlist.html',
                                m=False,
                                index=-1
@@ -140,7 +140,7 @@ def playlist():
     return jsonify({ 'items': items })
 
 def status():
-    if (var.playlist.length() > 0):
+    if len(var.playlist) > 0:
         return jsonify({'ver': var.playlist.version,
                         'empty': False,
                         'play': not var.bot.is_pause,
@@ -184,8 +184,6 @@ def post():
             if not folder.endswith('/'):
                 folder += '/'
 
-            print('folder:', folder)
-
             if os.path.isdir(var.music_folder + folder):
 
                 files = util.get_recursive_file_list_sorted(var.music_folder)
@@ -199,10 +197,10 @@ def post():
                     files = music_library.get_files(folder)
 
                 music_wrappers = list(map(
-                    lambda file: PlaylistItemWrapper(FileItem(var.bot, file), user),
+                    lambda file: PlaylistItemWrapper(FileItem(var.bot, folder + file), user),
                     files))
 
-                var.playlist.extend(files)
+                var.playlist.extend(music_wrappers)
 
                 for music_wrapper in music_wrappers:
                     log.info('web: add to playlist: ' + music_wrapper.format_debug_string())
@@ -213,7 +211,7 @@ def post():
             var.playlist.append(music_wrapper)
 
             log.info("web: add to playlist: " + music_wrapper.format_debug_string())
-            if var.playlist.length() == 2:
+            if len(var.playlist) == 2:
                 # If I am the second item on the playlist. (I am the next one!)
                 var.bot.async_download_next()
 
@@ -228,7 +226,7 @@ def post():
             music_wrapper = var.playlist[int(request.form['delete_music'])]
             log.info("web: delete from playlist: " + music_wrapper.format_debug_string())
 
-            if var.playlist.length() >= int(request.form['delete_music']):
+            if len(var.playlist) >= int(request.form['delete_music']):
                 index = int(request.form['delete_music'])
 
                 if index == var.playlist.current_index:
@@ -236,14 +234,14 @@ def post():
 
                     if index < len(var.playlist):
                         if not var.bot.is_pause:
-                            var.bot.interrupt_playing()
+                            var.bot.interrupt()
                             var.playlist.current_index -= 1
                             # then the bot will move to next item
 
                     else:  # if item deleted is the last item of the queue
                         var.playlist.current_index -= 1
                         if not var.bot.is_pause:
-                            var.bot.interrupt_playing()
+                            var.bot.interrupt()
                 else:
                     var.playlist.remove(index)
 
@@ -254,7 +252,8 @@ def post():
 
             if len(var.playlist) >= int(request.form['play_music']):
                 var.playlist.point_to(int(request.form['play_music']) - 1)
-                var.bot.interrupt_playing()
+                var.bot.interrupt()
+                time.sleep(0.1)
 
         elif 'delete_music_file' in request.form and ".." not in request.form['delete_music_file']:
             path = var.music_folder + request.form['delete_music_file']
@@ -272,16 +271,16 @@ def post():
         elif 'action' in request.form:
             action = request.form['action']
             if action == "randomize":
-                var.bot.interrupt_playing()
-                var.playlist.set_mode("random")
+                var.playlist = media.playlist.get_playlist("random", var.playlist)
+                var.bot.interrupt()
                 var.db.set('playlist', 'playback_mode', "random")
                 log.info("web: playback mode changed to random.")
             if action == "one-shot":
-                var.playlist.set_mode("one-shot")
+                var.playlist = media.playlist.get_playlist("one-shot", var.playlist)
                 var.db.set('playlist', 'playback_mode', "one-shot")
                 log.info("web: playback mode changed to one-shot.")
             if action == "repeat":
-                var.playlist.set_mode("repeat")
+                var.playlist = media.playlist.get_playlist("epeat", var.playlist)
                 var.db.set('playlist', 'playback_mode', "repeat")
                 log.info("web: playback mode changed to repeat.")
             elif action == "stop":
index 291af6cf37738e718d8d19010dd8886d0e7de3d5..d212d2aae8c09d75f6070973a1d817aa3cd901bf 100644 (file)
@@ -43,13 +43,34 @@ def dict_to_item(dict):
     elif dict['type'] == 'radio':
         return PlaylistItemWrapper(RadioItem(var.bot, "", "", dict), dict['user'])
 
-
-class PlayList(list):
-    def __init__(self, *args):
-        super().__init__(*args)
+def get_playlist(mode, _list=None, index=None):
+    if _list and index is None:
+        index = _list.current_index
+
+    if _list is None:
+        if mode == "one-shot":
+            return OneshotPlaylist()
+        elif mode == "repeat":
+            return RepeatPlaylist()
+        elif mode == "random":
+            return RandomPlaylist()
+    else:
+        if mode == "one-shot":
+            return OneshotPlaylist().from_list(_list, index)
+        elif mode == "repeat":
+            return RepeatPlaylist().from_list(_list, index)
+        elif mode == "random":
+            return RandomPlaylist().from_list(_list, index)
+
+    raise
+
+
+class BasePlayList(list):
+    def __init__(self):
+        super().__init__()
         self.current_index = -1
         self.version = 0  # increase by one after each change
-        self.mode = "one-shot"  # "repeat", "random"
+        self.mode = "base"  # "repeat", "random"
         self.pending_items = []
         self.log = logging.getLogger("bot")
         self.validating_thread_lock = threading.Lock()
@@ -57,19 +78,13 @@ class PlayList(list):
     def is_empty(self):
         return True if len(self) == 0 else False
 
-    def set_mode(self, mode):
-        # modes are "one-shot", "repeat", "random"
-        self.mode = mode
-
-        if mode == "random":
-            self.randomize()
+    def from_list(self, _list, current_index):
+        self.version += 1
+        super().clear()
+        self.extend(_list)
+        self.current_index = current_index
 
-        elif mode == "one-shot" and self.current_index > 0:
-            # remove items before current item
-            self.version += 1
-            for i in range(self.current_index):
-                super().__delitem__(0)
-            self.current_index = 0
+        return self
 
     def append(self, item: PlaylistItemWrapper):
         self.version += 1
@@ -95,9 +110,6 @@ class PlayList(list):
 
         return item
 
-    def length(self):
-        return len(self)
-
     def extend(self, items):
         self.version += 1
         super().extend(items)
@@ -110,29 +122,15 @@ class PlayList(list):
             return False
 
         self.version += 1
-        #logging.debug("playlist: Next into the queue")
 
         if self.current_index < len(self) - 1:
-            if self.mode == "one-shot" and self.current_index != -1:
-                super().__delitem__(self.current_index)
-            else:
-                self.current_index += 1
-
+            self.current_index += 1
             return self[self.current_index]
         else:
-            self.current_index = 0
-            if self.mode == "one-shot":
-                self.clear()
-                return False
-            elif self.mode == "repeat":
-                return self[0]
-            elif self.mode == "random":
-                self.randomize()
-                return self[0]
-            else:
-                raise TypeError("Unknown playlist mode '%s'." % self.mode)
+            return False
 
     def point_to(self, index):
+        self.version += 1
         if -1 <= index < len(self):
             self.current_index = index
 
@@ -142,25 +140,14 @@ class PlayList(list):
                 return index
         return None
 
-    def update(self, item, id):
-        self.version += 1
-        index = self.find(id)
-        if index:
-            self[index] = item
-            return True
-        return False
-
     def __delitem__(self, key):
         return self.remove(key)
 
-    def remove(self, index=-1):
+    def remove(self, index):
         self.version += 1
         if index > len(self) - 1:
             return False
 
-        if index == -1:
-            index = self.current_index
-
         removed = self[index]
         super().__delitem__(index)
 
@@ -170,9 +157,10 @@ class PlayList(list):
         return removed
 
     def remove_by_id(self, id):
+        self.version += 1
         to_be_removed = []
-        for index, item in enumerate(self):
-            if item.id == id:
+        for index, wrapper in enumerate(self):
+            if wrapper.item.id == id:
                 to_be_removed.append(index)
 
         for index in to_be_removed:
@@ -185,30 +173,16 @@ class PlayList(list):
         return self[self.current_index]
 
     def next_index(self):
-        if len(self) == 0 or (len(self) == 1 and self.mode == 'one_shot'):
-            return False
-
         if self.current_index < len(self) - 1:
             return self.current_index + 1
         else:
-            return 0
-
-    def next_item(self):
-        if len(self) == 0 or (len(self) == 1 and self.mode == 'one_shot'):
             return False
 
-        return self[self.next_index()]
-
-    def jump(self, index):
-        if self.mode == "one-shot":
-            for i in range(index):
-                super().__delitem__(0)
-            self.current_index = 0
+    def next_item(self):
+        if self.current_index < len(self) - 1:
+            return self[self.current_index + 1]
         else:
-            self.current_index = index
-
-        self.version += 1
-        return self[self.current_index]
+            return False
 
     def randomize(self):
         # current_index will lose track after shuffling, thus we take current music out before shuffling
@@ -240,17 +214,15 @@ class PlayList(list):
 
         items = list(var.db.items("playlist_item"))
         items.sort(key=lambda v: int(v[0]))
-        self.extend(list(map(lambda v: dict_to_item(json.loads(v[1])), items)))
-
-        self.current_index = current_index
+        self.from_list(list(map(lambda v: dict_to_item(json.loads(v[1])), items)), current_index)
 
     def _debug_print(self):
         print("===== Playlist(%d)=====" % self.current_index)
         for index, item_wrapper in enumerate(self):
             if index == self.current_index:
-                print("-> %d %s" % (index, item_wrapper.item.title))
+                print("-> %d %s" % (index, item_wrapper.format_debug_string()))
             else:
-                print("%d %s" % (index, item_wrapper.item.title))
+                print("%d %s" % (index, item_wrapper.format_debug_string()))
         print("=====     End     =====")
 
     def start_async_validating(self):
@@ -271,3 +243,106 @@ class PlayList(list):
 
         self.log.debug("playlist: validating finished.")
         self.validating_thread_lock.release()
+
+
+class OneshotPlaylist(BasePlayList):
+    def __init__(self):
+        super().__init__()
+        self.mode = "one-shot"
+        self.current_index = -1
+
+    def from_list(self, _list, current_index):
+        for i in range(current_index):
+            _list.pop()
+        return super().from_list(_list, -1)
+
+    def next(self):
+        if len(self) == 0:
+            return False
+
+        self.version += 1
+
+        if len(self) > 0:
+            if self.current_index != -1:
+                super().__delitem__(self.current_index)
+                if len(self) == 0:
+                    return False
+            else:
+                self.current_index = 0
+            return self[0]
+
+        else:
+            self.clear()
+            return False
+
+    def next_index(self):
+        if len(self) > 1:
+            return 1
+        else:
+            return False
+
+    def next_item(self):
+        if len(self) > 1:
+            return self[1]
+        else:
+            return False
+
+    def point_to(self, index):
+        self.version += 1
+        self.current_index = -1
+        for i in range(index + 1):
+            super().__delitem__(0)
+
+
+class RepeatPlaylist(BasePlayList):
+    def __init__(self):
+        super().__init__()
+        self.mode = "repeat"
+
+    def next(self):
+        if len(self) == 0:
+            return False
+
+        self.version += 1
+
+        if self.current_index < len(self) - 1:
+            self.current_index += 1
+            return self[self.current_index]
+        else:
+            self.current_index = 0
+            return self[0]
+
+    def next_index(self):
+        if self.current_index < len(self) - 1:
+            return self.current_index + 1
+        else:
+            return 0
+
+    def next_item(self):
+        return self[self.next_index()]
+
+
+class RandomPlaylist(BasePlayList):
+    def __init__(self):
+        super().__init__()
+        self.mode = "random"
+
+    def from_list(self, _list, current_index):
+        self.version += 1
+        random.shuffle(_list)
+        return super().from_list(_list, -1)
+
+    def next(self):
+        if len(self) == 0:
+            return False
+
+        self.version += 1
+
+        if self.current_index < len(self) - 1:
+            self.current_index += 1
+            return self[self.current_index]
+        else:
+            self.randomize()
+            self.current_index = 0
+            return self[0]
+
index ebc2a4cef63847350278be96c6b234ad5923bae2..b130568a6c196449d7e90c82107aa3f060544408 100644 (file)
@@ -122,6 +122,9 @@ class RadioItem(BaseItem):
     def format_current_playing(self, user):
         return constants.strings("now_playing", item=self.format_song_string(user))
 
+    def format_short_string(self):
+        return self.title if self.title else self.url
+
     def display_type(self):
         return constants.strings("radio")
 
index 908f27d90b59691b507ea5e0bb0342e242b1212c..1b26c789124df71403a49b9c602f8b678cd32e4b 100644 (file)
@@ -78,7 +78,7 @@ class URLItem(FileItem):
             # Check the length, useful in case of playlist, it wasn't checked before)
             log.info(
                 "url: " + self.url + " has a duration of " + str(self.duration) + " min -- too long")
-            self.send_client_message(constants.strings('too_long'))
+            self.send_client_message(constants.strings('too_long', song=self.title))
             return False
         else:
             self.ready = "validated"
index a2fe25217df93c5262417d21dba9a2b490016d10..ddf5beabf85bd84aea181ae42d0e2717ecd2f744 100644 (file)
@@ -29,7 +29,7 @@ import media.url
 import media.file
 import media.radio
 import media.system
-from media.playlist import PlayList
+from media.playlist import BasePlayList
 
 
 class MumbleBot:
@@ -472,19 +472,17 @@ class MumbleBot:
         self.log.info("bot: music stopped. playlist trashed.")
 
     def stop(self):
-        # stop and move to the next item in the playlist
+        self.interrupt()
         self.is_pause = True
-        self.interrupt_playing()
-        self.playhead = 0
-        var.playlist.next()
         self.log.info("bot: music stopped.")
 
-    def interrupt_playing(self):
+    def interrupt(self):
         # Kill the ffmpeg thread
         if self.thread:
             self.thread.kill()
             self.thread = None
         self.song_start_at = -1
+        self.playhead = 0
 
     def pause(self):
         # Kill the ffmpeg thread
@@ -522,7 +520,7 @@ class MumbleBot:
 
 
         if var.config.getboolean('bot', 'announce_current_music'):
-            self.send_msg(util.format_current_playing())
+            self.send_msg(var.playlist.current_item().format_current_playing())
 
         self.log.info("bot: execute ffmpeg command: " + " ".join(command))
         # The ffmpeg process is a thread
@@ -627,15 +625,6 @@ if __name__ == '__main__':
     bot_logger.addHandler(handler)
     var.bot_logger = bot_logger
 
-    var.playlist = PlayList() # playlist should be initialized after the database
-    var.bot = MumbleBot(args)
-    command.register_all_commands(var.bot)
-
-    # load playlist
-    if var.config.getboolean('bot', 'save_playlist', fallback=True):
-        var.bot_logger.info("bot: load playlist from previous session")
-        var.playlist.load()
-
     # load playback mode
     playback_mode = None
     if var.db.has_option("playlist", "playback_mode"):
@@ -644,7 +633,17 @@ if __name__ == '__main__':
         playback_mode = var.config.get('bot', 'playback_mode', fallback="one-shot")
 
     if playback_mode in ["one-shot", "repeat", "random"]:
-        var.playlist.set_mode(playback_mode)
+        var.playlist = media.playlist.get_playlist(playback_mode)
+    else:
+        raise KeyError("Unknown playback mode '%s'" % playback_mode)
+
+    var.bot = MumbleBot(args)
+    command.register_all_commands(var.bot)
+
+    # load playlist
+    if var.config.getboolean('bot', 'save_playlist', fallback=True):
+        var.bot_logger.info("bot: load playlist from previous session")
+        var.playlist.load()
 
     # Start the main loop.
     var.bot.loop()
index b677ac941e7577d067a2b604176be83df8972d31..03bf9ffaa1e905dba39c169d105596df72b8a7f3 100644 (file)
             <span class="badge badge-secondary">{{ m.display_type() }}</span>
             <br>
             {% if m.type == 'file' %}
-                {{ m.artist }}
+                {% if m.artist %}
+                    {{ m.artist }}
+                {% else %}
+                    ??
+                {% endif %}
             {% elif m.type == 'url_from_playlist' %}
                 <a href="{{ m.playlist_url }}"><i>{{ m.playlist_title|truncate(50) }}</i></a>
             {% else %}