Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seperate Invidious playlists #114

Closed
omarroth opened this issue Aug 16, 2018 · 2 comments · Fixed by #673

Comments

@omarroth
Copy link
Owner

commented Aug 16, 2018

For #18 it's necessary for each video in a playlist to hold extra data, essentially a edges field that would hold an array of videos (or playlist) that the current video can link to. An example of a video in a playlist would look like:

{
  "title":  "Example title",
  "id":     "AAAAAAAAAAA",
  "author": "Example author",
  "ucid":   "UCAAAAAAAAAAAAAAAAAAAAAA",
  "edges":  ["AAAAAAAAAAB", "PL0000000000000000"],
}

After watching the video, the client can choose between the video AAAAAAAAAAB, or playlist PL0000000000000000. I'd recommend taking a look at https://www.python.org/doc/essays/graphs/ for more info on how this would be implemented. Complex loops and other behavior would be made possible with this structure that wouldn't be possible with YouTube's lists.

Assuming the next video is chosen randomly from the edges provided, it should be possible for the user to specify an edge multiple times, so e.g. specifying ["AAAAAAAAAAA", "AAAAAAAAAAA", "AAAAAAAAAAB"] in the example above would have a 2/3 chance of looping the video again.

In addition, this would allow an Invidious user to create their own playlists. A Favorites and Watched playlist could be provided like this.

I'd be interested in hearing feedback about how this should be implemented to provide as much flexibility and functionality to users.

@Discookie

This comment has been minimized.

Copy link

commented Aug 16, 2018

There's got to be a better way of specifying weights - perhaps each edge can have an optional weight, like

"edges": 
[
    "AAAAAAAAAAB",
    {
        "id": "AAAAAAAAAAC",
        "weight": 3 # default weight: 1
    },
    "PL0000000000000000"
]

We'd also need to specify that we play a random video from the playlist-edge, or we play from the N-th video.

Also we'll probably need to specify something like an inverse selection, such as "Play the next video from PL0...00, but don't play a video that's in PL0...01!"

"edges": 
[
    {
        "id": "PL0000000000000000",
        "start": "random", # can also give a number to start from
        "inverse":
        [
            "AAAAAAAAAAB",
            "PL0000000000000001"
        ] # don't play any of these, if you decide on this playlist
    },
    {
        "id": "AAAAAAAAAAB",
        "weight": 8
    }
]

A practical example:
Think of a 500-long PL_FAVORITE_MUSIC playlist, with various genres.
The next video should be a video of the same genre with a 0.95 chance, and a video of a different genre with 0.05 chance.

In this case, you'll have to specify many things to add a new music to PL_FAVORITE_MUSIC:

  • You need to sort music into playlists by genres, the current genre is PL_THIS_GENRE.
  • For the new video, you need to specify an edge to PL_THIS_GENRE such as:
    • We play a random video from this list.
    • We don't play the same video twice.
    • We should still stay inside PL_FAVORITE_MUSIC instead of jumping into PL_THIS_GENRE!
  • You also need to specify an edge to PL_FAVORITE_MUSIC
    • Again, that we play a random video
    • We don't play videos from PL_THIS_GENRE. (What if 90% of your music is from one genre? You'd never listen to the other ones!)

There's some edge-cases, like a genre only having 5 songs. In this case, a .95 chance will be very repetitive, so we need to adjust the weights better. But when we add a 6-th song, we need to re-adjust the weight of PL_THIS_GENRE for each video inside a genre.


Another practical example:
Collect all videos from LinusTechTips in a random list, but if we start a series (like Scrapyard Wars or Mining Adventures), we should start it from the beginning, and watch the entire series. Let's name this playlist PL_LINUSTECH

In this case, you'll have to specify a few things to add a new upload to PL_LINUSTECH:

  • If the video is the pilot of a new series, you'll need to create a new playlist, let's name it PL_SCRAPYARD_WARS_S5!
    • This playlist should always play in order.
    • At the end of the playlist, we need to act as if we're NOT part of a series, see below.
  • If the video is continuing a series, you'll need to do the following:
    • Add the video to an aux. playlist called PL_SERIES
      • We use this playlist to not hop into the middle of a series
    • Add the video to its respective series playlist, such as PL_SCRAPYARD_WARS_S5!
      • This video will become the new end of the series, so we'll need to adjust the previous video, and also make this video act like it's not part of a series.
  • If the video is NOT part of a series, you'll need to specify the following:
    • The next video should only be a standalone video, or a first episode from a series.
      • We can do that by not playing a video that's in PL_SERIES. This will include pilots but exclude every other episode.

For extra pain, we can also specify a weight needed to start a series. They are long, and thus they should be started less frequently than a standalone video. For example, This could be done with a PL_PILOTS playlist.
In this case, the playlist's overall weight needs to scale with the playlist's length - if we have 3 videos in a 0.8-weight playlist, each video shouldn't have the overall weight of 0.26, but the overall weight of 0.8.


I know all of these examples are advanced, and take time to maintain, but let's be fair - a regular playlist gets the job done 99.9% of the time, and we can't really avoid any of the steps listed here.

Also these examples highlight the need for practical functionality such as copy-paste, a fast and easy interface for selecting edges and weights, changing edges on many videos at the same time, and creating "utility" playlists that should not appear as proper playlists.


You could also create your own version of "Mix" from videos using this system - play the first recommended video with a 1/2 chance, play the second with a 1/4 chance, third with 1/8 and so on.

@omarroth

This comment has been minimized.

Copy link
Owner Author

commented Aug 16, 2018

Thank you very much for your thoughts! Obviously something like this can quickly become difficult to reason about, but I think there's so many useful applications it's worth exploring.

Some thoughts on handling the examples outlined:


Each node has certain attributes, such as weight, id, title, etc.

I think a client would have to keep track of their path, so for example if I have a playlist:

[
  {
    "id": "AAAAAAAAAAA"
  },
  {
    "id": "PL0000000000000001",
  },
  {
    "id": "AAAAAAAAAAB"
  }
]

After finishing PL0..01, the client would have to know to go back to the original playlist. I'm curious how that could be implemented. Further down I describe adding a node to PL0..01 which goes back to the original playlist, but I can see this having issues when trying to have playlists from multiple channels all grouped together.

I agree having weight as an attribute is much more concise. To clarify the first example for myself as well as for others: video AAAAAAAAAAC would have a 3/5 probability of being selected, with the others being 1/5.

I think accessing playlists in the way that is suggested above could also be done as attributes. For example:

"edges":
  [
    {
      "id": "PL0000000000000001",
      "index": 10,
      "order": "random"
     // "order": "list"
  ]

Which would start the playlist PL0..01 at index 10. Assuming the edges of PL0..01 don't have their own edges, the above would select a random video to play.


Here's how I would work through the first example:
The edge of every video in each PL_GENRE would look like:

"edges": [
  {
    "id": "PL_THIS_GENRE",
    "weight": 95,
    "order": "random_unique" // Similar to the example above
  },
  {
    "id": "PL_FAVORITE_MUSIC",
    "weight": 5
  }
]

Then PL_FAVORITE_MUSIC would look like:

"edges": [
  {
    "id": "PL_GENRE1"
  },
  {
    "id": "PL_GENRE2"
  },
  //...
]

Which should do everything that you outlined. Wanting to add a new video to PL_FAVORITE_MUSIC would require you to add that video to the PL_GENRE with the nodes being the same as what's shown above.

I can see keeping track of playlists in this way quickly becoming cumbersome though.


For the second example I would create the playlist PL_LINUSTECH with all the videos on the channel in addition to all the playlists. The elements in each PL_SERIES would look something like:

"edges": [
  {
    "id": "AAAAAAAAAAA"
  },
  {
    "id": "AAAAAAAAAAB"
  },
  //...
  {
    "id": "PL_LINUSTECH",
    "order": "random"
  }
]

Which should hopefully do almost everything specified. To add a new video to the playlists, you would add it to PL_LINUSTECH and then add it to the respective PL_SERIES, or create a new one and add the new playlist to PL_LINUSTECH.


I can see this becoming a very difficult thing to reason about and control. To start to implement this, I would probably only make it possible to do the following:

"edges": [
  {
    "id": "AAAAAAAAAAA"
  },
  {
    "id": "AAAAAAAAAAB",
    "weight": 4,
  }
]

Which provides minimal functionality but I think is still useful. The main issue I think is handling everything properly on the client side. A client has to juggle quite a bit of information. I can quickly see something like this becoming Turing-complete, which could almost be achieved by some of the features mentioned.

For managing and creating these playlists, it would probably be easiest to type everything by hand, like what we've been doing here, and have that as something closer to an advanced feature, leaving add, remove, etc. features as things that can be done through a GUI easily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.