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

Playing recently played songs over and over again. #333

Closed
Botanistt opened this issue May 26, 2016 · 16 comments
Closed

Playing recently played songs over and over again. #333

Botanistt opened this issue May 26, 2016 · 16 comments
Labels

Comments

@Botanistt
Copy link

Is there a way to skip recently played songs? It's frusturating because my playlist isn't huge and pretty often I hear same song over and over again. If there was a way to skip certain song if it was recenlty played that would be awesome.

@toots
Copy link
Member

toots commented May 26, 2016

playlist operator has a check_next parameters that could be used to achieve that. Otherwise, you might be better off using request.dynamic..

@toots toots closed this as completed May 26, 2016
@Botanistt Botanistt mentioned this issue May 28, 2016
@Botanistt
Copy link
Author

I tried this
radio = playlist(id="playlist",check_next="true",length=3600.0, default_duration=1800.0,timeout=90.0,mode="random",reload=900,reload_mode="seconds",mime_type="audio/x-mpegurl","/radio/top40/files/")
and it didn't worked. What's the correct syntax for this?

@smimram smimram reopened this May 29, 2016
@smimram smimram added the usage label May 29, 2016
@Botanistt
Copy link
Author

anyone?

@giflw
Copy link

giflw commented May 31, 2016

Hi! Take a look at issue #197. The check_next parameter must receive a callback function.

@Botanistt
Copy link
Author

Thanks @giflw for response but I have no idea how it's supposed to look.
I'm so confused at this point. :(

@Botanistt
Copy link
Author

I tried to add this at playlist
check_next=(requests('a))
but it says empty token.
Requests are defined like this
requests = request.queue(id="requests")

@giflw
Copy link

giflw commented Jun 8, 2016

Hi!

As I don't know how much you know about liquidsoap I'll do a brief introduction here. As OCaml, liquidsoap is functional. So you don't have mutable states by default. All is function or immutable values. The type ref is the only mutable structure in liquidsoap. As functions are first class citizens, you can pass a function as parameter. Sometimes it will work as a callback.

Well, in this case, check_next parameter will receive a function to use as a callback. So, you need to give a function referece to that parameter: check_next = callback_function_name.
The function must have a specific signature and a specific return type.

Now from the docs about playlist we have ?check_next:((request('a))->bool. The ? means check_next is optional. The value for that parameter is ((request('a))->bool, meaning it receive a function that accepts a request and returns a bool.

From issue #197:

prev = ref("")

def checker(r)
  metadata = request.metadata(r)
  print("PLAYLIST #{prev} -> #{metadata['filename']}")
  if metadata["filename"] == "" then true else
    if metadata["filename"] == !prev then false else
      prev := metadata["filename"]
      true
    end
  end
end

pls = playlist(check_next=checker, "/tmp/music")
out(pls)

The first line, prev = ref(""), creates a mutable reference to a empty string. In this way, we can use it to store the last played media (the previous one). As nothing has been played, it contains an empty string.

Next we define a function that accept one argument: checker(r). r will be of type request. From request, we can get the metadata. Metadata is a map or dictionary or an array with string index, as you wish to call. Metadata contains a property called filename. If it's blank, we let it pass. If it's different from previous (stored in the prev ref) we let pass too. But if metadata filename is equals to prev ref value, we skip returning false. One last thing before returning: store the new metadata filename in the prev ref, so next media can be compared too.

This check next function uses metadata filename, but you can use anything that you can get from the given request.

I hope this help.

@toots
Copy link
Member

toots commented Jul 6, 2017

Awesome, thanks @giflw !

@wnolfm
Copy link

wnolfm commented Oct 26, 2017

I'm running liquidsoap 1.3.3 on Debian 9, and I'm getting the following:

Oct 26 01:19:56 octave liquidsoap[16306]: Starting liquidsoap channels: radio.liq no more csLADSPA plugins
Oct 26 01:19:56 octave liquidsoap[16306]: At line 25, char 43: cannot apply that parameter because the function
Oct 26 01:19:56 octave liquidsoap[16306]:   (at line 25, char 24) has no argument labeled "check_next"!
Oct 26 01:19:56 octave liquidsoap[16306]: OK

I am using the following:

def checker(r)
   metadata = request.metadata(r)
   print("PLAYLIST #{prev} -> #{metadata['filename']}")
   if metadata["filename"] == "" then true else
      if metadata["filename"] == !prev then false else
         prev := metadata["filename"]
         true
      end
    end
end

music = playlist(check_next=checker, "/srv/music")

Any ideas?

@gilou
Copy link
Contributor

gilou commented Apr 7, 2018

Hi, as told on the mailing list, I do use a trick: grepping through the logs to do that:
https://gist.github.com/gilou/daa2fb2e4b53074eb50bdd9dea417b04

@smimram
Copy link
Member

smimram commented May 13, 2021

For info, I have just introduced playlog operator, which can be used to record when a song was last played. For instance, you can reject all songs played less than an hour ago with

l = playlog()
def check(r)
  m = request.metadata(r)
  if l.last(m) < 3600. then
    log.info("Rejecting #{m['filename']} (played #{l.last(m)}s ago).")
    false
  else
    l.add(m)
    true
  end
end
s = playlist(check_next=check, "playlist")
output(s)

@gilou
Copy link
Contributor

gilou commented May 24, 2021

Damn… That code was from 2018, nice… But in fact, I updated it quite a lot, and my repeat was written in a not so elegant way,
before we had a few of nice operators… here: https://gist.github.com/gilou/45f636c04e7a74d5a01946a49167363e
But that lacks persistence, and I'm guessing, so does your playlog, but I haven't checked further… How can we avoid repetition upon restart of liq easily ? my grep stuff did it "by default", but I'm not sure that's the best way…

Edit: I have read the playlog() niceness, but… is this not growing forever? I'm a bit worried that for streams I have, that literally ran for years, that might be an issue… And I think my implementation was not that nice either, but I tried to address cleaning up after a given time, so I don't retain the playlog for longer than needed.

Could we set a playlog(persistent_log="/tmp/myfile.txt") or something?

Also… It brings another question I had in mind, that might be out of scope here: you set hash() to use the filename, what would be the nicest way to be able to deal with something like an actual md5sum() of the file itself throughout (I'm thinking about storing unique ids, rather than the filename, and not mistaking 2 same files with a different name…). Adding the md5sum or acoustic id in the metadata ? :P

@oranjebloom
Copy link

@gilou my understanding is that the playlog that @smimram has provided uses the cache, and not the log file, so it would retain recently played items even in the instance of a restart. Perhaps he will be able to confirm that.

@smimram
Copy link
Member

smimram commented May 25, 2021

Let me try to answer this:

  • there is currently no persistency although it would be easy to add, I'll do this and keep you posted
  • about the growing issue: there is a duration parameter which basically says "forget files older than this duration" to avoid growing logs (the default value is infinity which means that files are never forgotten though)
  • if you want an md5sum of the file you can use file.digest to compute it, but yes I think that a much more satisfactory answer would be to add this to the metadata instead of computing it each time

smimram added a commit that referenced this issue May 25, 2021
@smimram
Copy link
Member

smimram commented May 25, 2021

I just added a persistency option to playlog which enables you to specify a filename where the values are saved and reloaded at next start.

@smimram
Copy link
Member

smimram commented Jul 26, 2021

Closing this since playlog seems to be a satisfactory solution.

@smimram smimram closed this as completed Jul 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants