New podcast enqueueing method + example Python code

App version: 3.4.0 (state whether from Google Play/F-Droid/Custom built APK)

Problem you may be having, or feature you want:

(TLDR at the end)
Hi, in my podcasts queue, my objective is to stay updated on podcasts of which I’ve finished the backlog, while also continuing listening through the backlog of my new subscriptions; the method i’ve found most effective is the following: (i’ll start representing the queue as a string of lowercase characters in which the same letter always represents an episode from the same podcast; podcasts are represented by corresponding uppercase characters)

I always have two or three podcasts, which I’ll call “current main”, which I’m currently listening through the backlog of.

In the queue, I alternate through an episode of each podcast, so the queue in a vacuum looks like this (in this example, i have 2 current main podcasts):

Current queue: [ababababab]

And i think of each repetition of the first letter of the queue as the beginning of a new cycle; so the current queue is divided into 5 identical cycles:

Current queue: [ababababab]
Cycle 1: [ab]
Cycle 2: [ab]
Cycle 3: [ab]
Cycle 4: [ab]
Cycle 5: [ab]

I’m also subscribed to to lots of other podcasts; so whenever another episode comes out, let’s say from podcast C, i add it to the queue at the end of the first cycle:
Previous queue: [ababababab]
New episode: [c]
Current queue: [abcabababab]
Cycle 1: [abc]
Cycle 2: [ab]
Cycle 3: [ab]
Cycle 4: [ab]
Cycle 5: [ab]

as you can see, the cycle division works in the same way as before: each episode from podcast A marks the beginning of a new cycle

when podcast D comes out with a new episode, I do the same thing:
Previous queue: [abcabababab]
New episode: [d]
Current queue: [abcdabababab]
Cycle 1: [abcd]
Cycle 2: [ab]
Cycle 3: [ab]
Cycle 4: [ab]
Cycle 5: [ab]

If podcast C were to come out with another episode, since c is already present in the first cycle, i add it to the second cycle, but always matching the order of the first cycle:
Previous queue: [abcdabababab]
New episode: [c]
Current queue: [abcdabcababab]
Cycle 1: [abcd]
Cycle 2: [abc]
Cycle 3: [ab]
Cycle 4: [ab]
Cycle 5: [ab]

As always we divide the queue into cycles again.

Now, we are talking about a podcast queue; so what’s going to happen is that I’ll eventually finish listening to the first episode in the queue:
Previous queue: [abcdabcababab]
Current queue: [bcdabcababab]
Cycle 1: [bcda]
Cycle 2: [bca]
Cycle 3: [ba]
Cycle 4: [ba]
Cycle 5: [b]
This time, the cycle division is performed before episodes of podcast B, which is now the first podcast of the queue

Let’s simulate another listen:
Previous queue: [bcdabcababab]
Current queue: [cdabcababab]
Cycle 1: [cdab]
Cycle 2: [cab]
Cycle 3: [ab]
Cycle 4: [ab]

As you can see, the division now starts from podcast C, but since C is not present in every cycle, the cycles in which it’s absent are divided starting from the second episode, d; since d also doesn’t appear in all cycles, we end up relying on episodes from podcast A; a and b episodes will appear in every cycle since they are from the current main podcasts. We also emptied Cycle 5, making it disappear

If D were to come out with another episode, it would be added to the second cycle, since it is already present in the first, and its position in the cycle will match the position episode d has in Cycle 1:
Previous queue: [cdabcababab]
New episode: [d]
Current queue: [cdabcdababab]
Cycle 1: [cdab]
Cycle 2: [cdab]
Cycle 3: [ab]
Cycle 4: [ab]

Let’s simulate another listen:
Previous queue: [cdabcdababab]
Current queue: [dabcdababab]
Cycle 1: [dabc]
Cycle 2: [dab]
Cycle 3: [ab]
Cycle 4: [ab]

As you can see, all letters in each cycle are contained in the same order in the previous cycles; even if some podcasts’ episodes are missing from the last cycles, the order is always preserved in all cycles; Cycle 1 “dictates” the order for all following cycles

If I were to add to the queue an episode from a podcast that is present in every cycle, a new cycle would be created at the end; let’s see it by adding an episode from podcast A
Previous queue: [dabcdababab]
New episode: [a]
Current queue: [dabcdabababa]
Cycle 1: [dabc]
Cycle 2: [dab]
Cycle 3: [ab]
Cycle 4: [ab]
Cycle 5: [a]

if i did it again, this would be the result:
Previous queue: [dabcdabababa]
New episode: [a]
Current queue: [dabcdabababaa]
Cycle 1: [dabc]
Cycle 2: [dab]
Cycle 3: [ab]
Cycle 4: [ab]
Cycle 5: [a]
Cycle 6: [a]

TLDR: I’d like another option in the “Settings > Playback > Enqueue location” list of options (let’s call it “CycleSort”) that performs the “add” method in the python code below (above an in-depth explanation with examples)

Working Python code:

class PodcastQueue:
def init(self):
self.queue = “” # Initialize an empty string to represent the podcast queue
self.cycles = # Initialize an empty list to store the cycles

def divide(self):
    # This method organizes the queue into cycles

    reference = []  # Reference cycle to help in comparison
    self.cycles = []  # Reset the cycles

    for letter in self.queue:
        # Iterate over each episode in the queue
        added = False  # Flag to check if the episode is added to any cycle
        for i, cycle in enumerate(self.cycles):
            # Check each cycle to see if the episode can be added
            reference = self.cycles[0][:]  # Start with a reference of the first cycle
            if letter not in cycle:
                # If this is the first cycle
                if i == 0:
                    # Append the episode to the first cycle
                    cycle.append(letter)
                    added = True
                    break
                else:
                    # Add the episode to the first cycle in which it doesn't already appear; in doing so,
                    # make sure that the order of the podcasts matches the first cycle, even if some podcasts are missing
                    reference = [ref_letter for ref_letter in reference if ref_letter in cycle or ref_letter == letter]
                    cycle[:] = reference
                    added = True
                    break
        if not added:
            # If the episode wasn't added to any cycle, create a new cycle and add it there
            self.cycles.append([letter])

    # Reconstruct the queue from the cycles
    self.queue = "".join("".join(cycle) for cycle in self.cycles)

def add(self, new_episodes):
    # This method adds new episodes to the queue and then divides it into cycles
    previous_queue = self.queue  # Store the current queue for printing later
    self.queue += new_episodes  # Append new episodes to the queue
    self.divide()  # Divide the updated queue into cycles
    self.print_status(previous_queue)  # Print the status of the queue

def listen(self):
    # This method simulates listening to the first episode in the queue
    previous_queue = self.queue  # Store the current queue for printing later
    if self.queue:
        # If the queue is not empty, remove the first episode
        self.queue = self.queue[1:]
        self.divide()  # Re-divide the queue into cycles after removing an episode
    self.print_status(previous_queue)  # Print the status of the queue

def print_status(self, previous_queue):
    # This method prints the previous and current status of the queue and cycles
    print(f"Previous Queue: [{previous_queue}]")
    print(f"Current Queue: [{self.queue}]")
    for i, cycle in enumerate(self.cycles):
        print(f"Cycle {i + 1}: [{''.join(cycle)}]")

def run(self):
    # This method runs an interactive loop to add episodes or listen to them
    while True:
        command = input("Enter command (add/lstn/exit): ").strip().lower()
        if command == 'add':
            new_episodes = input("Enter new episodes string: ").strip()
            self.add(new_episodes)
        elif command == 'lstn':
            self.listen()
        elif command == 'exit':
            break
        else:
            print("Invalid command, please try again.")

if name == “main”:
pq = PodcastQueue()
pq.run()

This seems quite similar to what the settings “keep sorted” and “smart shuffle” already do

2 Likes

I’ve just tried it, but it doesn’t sort it in the same way; for instance, in my specific case “smart shuffle”, while it did alternate between podcasts so not to have the same podcast twice, it also ordered the episodes within each podcasts in reverse chronological order.
The feature I had in mind was an additional option in the “Enqueue location” option list, so that every episode added to the queue would end up in the spot based on the aforementioned rules

I appreciate that you detailed how your request might be implemented, even including sample code. Your request reminds me of this solution to a somewhat similar feature request that just involved a configuration change:

2 Likes

Thanks for the quote, @Listener, but I think the solution is the very first one @ByteHamster already provided:

Here is a brief explanation of smart shuffle @ByteHamster already provided 4 years ago (TIL I learned what @Jack_o_Bong is asking for is called round robin… Even the notation @ByteHamster used is the same!).

But, sure, once smart shuffle is selected and keep sorted is checked, then set AP to auto-add new episodes to the queue, or, alternatively, leave them in the Inbox, and set it this way to have it automatically downloaded too.

4 Likes

You’re right, it is exactly like the round-robin feature; I guess this wasn’t as new/uncommon as I thought :sweat_smile:
Would it be possible to add the (non-spread out) round-robin to the already present sorting methods? I’m not in a rush about it, I imagine this isn’t top-priority, it would just be extremely convenient

I don’t think we should add an additional sorting method that is an additional variation of “smart shuffle”. As we state on our website, AntennaPod aims to be simple to use. These sorting options are not something average users immediately understand without half a screen of explanation. We should not present options to users that they don’t understand (especially if there are multiple options with only subtle differences)

2 Likes

I understand.
Let me know if anyone on the team reconsiders! :slight_smile:
Thanks for all the work y’all do