How do I embed a list of MP3s queried from dataview (or dataviewjs, query)

What I’m trying to do

I have multiple notes with metadata (YAML tags) and each note corresponds to a single mp3 file. Both the note and the mp3 have the same name. I’d like to dataview/dataviewjs/query a directory of notes and then based on the metadata have those mp3’s listed as playable files in the page.

I can get this to work with images but not mp3s.

Things I have tried

  1. I can have an mp3 playable if I manually type it, like this

![[song.mp3]]

  1. I can also use dataview to grab images from notes and display those using the embed(link), like this
LIST embed(link(file.name + ".jpg")) 
FROM ("dir1" or "dir2") and #tagA/subtag
sort file.name asc
  1. But when I try to use technique #2 with .mp3s, it doesn’t work.

It just lists the results as text. Perhaps this is a feature request for dataview development. I already put in a request here.

But I’m curious if anyone has a workaround, especially with query or dataviewjs.

I searched for mp3, embed, didn’t find anything regarding this.

1 Like

This creates a link to the file, making it easy to collate and order mp3 files and then play them:

---
mp3: "[[Born Slippy.mp3]]"
---

```dataview
TABLE WITHOUT ID
	mp3
FROM 
	"_inbox"
WHERE
	mp3
```

Thank you. Appreciated!

Your solution does give me a list of mp3 files, but it doesn’t fix my main issue; I’d like a page with all the queried songs embedded. (see image below)

Other goals:

  • The obsidian media player doesn’t play the next song on the page, it just stops after the first song. But I’ll deal with that issue next.
  • I’d want to use dataviewjs to randomize the song list order.

The end result would be a way to generate randomized, dynamic playlists based on metadata.

Thanks again for the help.


2 Likes

Yeah, and it opens playback up in a popup that disappears if you move your mouse.

I’m interested in this too.

I thought I had opened up a feature request to show the name of mp3 attachments above the attachment, but I can’t find it. That’s the minimum I’m hoping for, but I’d love your solution too!

I think the way forward would be to use a combination of Buttons + Templater + Dataview. The gist of the idea is as follows:

  • On a given page, have a button which replaces all text after the button with the result from a Templater command
  • The Templater command (based upon a template) triggers a Dataview script which collates the mp3-files in a random order using markdown syntax, which the template appends to the current file

This will give a static version of the embedded files, which will circumvent the issues that Dataview has with embedding files, whilst still allowing for a refresh of the playlist by the hit of a button.

Is this idea something you’re able to implement yourself? I’m running a little low on time tonight, so I need to do something else. If you’re not able to do it yourself, I might look into it one of the next few days.

1 Like

I’ve made a dvjs view that might interest you: some-custom-dataviews/jukebox at master · Krakor92/some-custom-dataviews · GitHub

It renders the mp3 files in a gallery view with autoplay automatically enabled. Note that each audio card have a very limited number of options:

  • Play/Pause
  • Timeline without timecode

This is by design because I don’t need to change the playback speed or volume of each file.

The readme should answer most of your questions, but let me know if you have any.

1 Like

Thank you for your reply! I’ll try Krakor’s dataview implementation first. I’m not a programmer, so your suggestion is beyond me. Let me test and report back.

Thank you!! Just seeing this. Let me test this today and report back.

@Krakor , I’ve been testing your jukebox. It is great!

Here’s what works for me so far:

  • Grabs songs from a specified directory
  • Filters them based on tags
  • Sorts them as shuffled
  • Lists them all on the page
  • All the songs play in a row

This is amazing! Thank you! This already does pretty much everything I wanted!

If you have time, I posted some questions at the bottom. Ran into some tech issues.

First though, here is my setup with a code example. Posting for others that might see this thread too.

The settings I updated in the view.js file:

const DEFAULT_FROM = '#🎼 AND "jukebox/songs"'
const DEFAULT_SCORE_DIRECTORY = "jukebox/DB"
const DEFAULT_THUMBNAIL_DIRECTORY = "jukebox/thumbnails"

The dataview code to filter and shuffle the songs:

```dataviewjs
await dv.view("_js/jukebox", {
	filter: {
		tags: [
			"#music/year/1970s",
			"#music/rock",
			]
	},
	sort: {
		shuffle: true,
	}	
})

The Dataview settings:

  • I disabled “Automatic View Refreshing” like you suggested in the documentation. It was tripping up playback while I was editing other obsidian notes.

And a screenshot of the Jukebox.


However, I did run into some issues if you have time to point me in the right direction.

  1. I think the playback controls are missing. I saw reference to them in the view.js, but they aren’t showing up for me. Any ideas?

  2. The top left icon opens up youtube to the default page. If I want to remove that icon, since I don’t need it, is there a way to do that in the view.js?

  3. Same for the top right icon (and bottom icon), which just give me an error. I’m not sure what they are trying to do anyway.

image

In the mean time, I’ll read through your documentation again and see if I can figure it out.

Thanks again, this is a great setup! Works perfectly on mobile too.

Thank you for the review, I’m really glad to hear that it meets your needs :blush:
However, the screenshot you showed looks very weird, like the css is not working at all…

Did you copy the view.css file from the repository and put it in the same folder as the view.js file?


To answer your questions:

  1. The readme controls should display correctly with the css. I added an image in the readme to show how it should look like

  2. For your second question, if the css was working correctly, the icons would not be displayed in the first place because the thumbnail behavior “override” them.
    However if you don’t have the THUMBNAIL_IS_URL_LINK constant set to true, you could set HIDE_ICONS to true in the view.js to hide them

  3. And for your last point, you could add the disable string “addscore” in your codeblock to remove the + button at the top and bottom of the grid.
    That make me think that it would be nice to add a global disable constant option in the view.js to have it applied globally. I’ll consider adding that at some point.

Also, you made me realize that I didn’t specify what these buttons do in the readme, so I added an explanation.

  1. I had the wrong view.css. I was using this one.
https://github.com/Krakor92/some-custom-dataviews/blob/master/gallery/view.css

Once I copied the correct one, the jukebox looks like this.

I defined the thumbnails like this in the YAML of the song page.

thumbnail: "[[BLT.jpg]]"
  1. Setting these two variables did not hide the icon, but that’s ok for my purposes.
const HIDE_ICONS = false
const THUMBNAIL_IS_URL_LINK = false
  1. That “disable addscore” code worked well.
await dv.view("_js/jukebox", {
	filter: {
		tags: [
			"#music/year/1970s",
			"#music/rock",
			]
	},
	sort: {
		shuffle: true,
	},
	disable: "addscore"
})

Thanks again!

@Krakor

Is it possible to do OR with the tag syntax? For example, if I want all rock songs and disco songs?

This would match songs that are both rock and disco

		tags: [
			"#music/rock",
			"#music/disco",
			]

Can I do something like this to grab songs that are either rock or disco?

		tags: [
			"#music/disco" OR "#music/rock",
			]

And only songs from the 1970s that are either rock or disco

		tags: [
			"#music/year/1970s",
			"#music/disco" OR "#music/rock",
			]

I was expecting you to ask this question, but unfortunately it is not easily achievable at this time. I would have loved to implement a natural language to do it like you showed but I don’t have the same ambitions as Dataview and it would have been way too much work to implement my own DSL parser in pure javascript just for this use case.

Anyway, what you could do is use the function filter option to filter manually with your custom logic. Note that I have not yet documented the query.js file or provided examples on how to use it. But here is how to write the OR logic for your examples:

await dv.view("_js/jukebox", {
	filter: (qs) => {
		const from = '#🎼 AND "jukebox/songs"'

		const onlyRock = qs.from(from)
		  .withTags("#music/rock")
		  ._pages

		const onlyDisco = qs.from(from)
		  .withTags("#music/disco")
		  ._pages

		qs.setPages(qs.joinPages(onlyRock, onlyDisco))
		  .withTags("#music/year/1970s")
	},
	sort: {shuffle: true},
	disable: "addscore",
})

You can delete or comment out with // the last .withTags(...) line to disable the AND logic.

Thank you, I think this should still be ok for my purposes. Is there a straight forward way to make it take N number of tags? I tested a couple ways but none of them worked.

await dv.view("_js/jukebox", {
	filter: (qs) => {
		const from = '#🎼 AND "jukebox/songs"'

		const tagA = qs.from(from)
		  .withTags("#music/rockA")
		  ._pages

		const tagB = qs.from(from)
		  .withTags("#music/rockB")
		  ._pages

		const tagC = qs.from(from)
		  .withTags("#music/rockC")
		  ._pages

		// Combine two tags (works)
		//qs.setPages(qs.joinPages(tagA, tagB))

		// Combine three or more tags

		// Test 1
		//qs.setPages(qs.joinPages(qs.joinPages(tagA, tagB), tagC))

		// Test 2
		//const tmpA = qs.joinPages(tagA, tagB)
		//qs.setPages(qs.joinPages(tmpA, tagC))
		
		// Filter with tagC
		//.withTags("#tagD")
	},
	sort: { shuffle: true },
	disable: "addscore"
})

Also, regarding your jukebox in general, is this something you’d be ok with me sharing in another thread? I think it is useful and other people would probably appreciate it. But if you were planning to roll it out, I’d defer to your timeline for that.

Ah yes, that’s based on the way i do it :slight_smile:

To explain briefly, this is because this function expects a Dataview array as input but the array returned by this function is not a Dataview array.

I’ve just pushed a new query.js to make this function a variadic one (that means it can take as many params as you want) and also to support non dataview array in entry.

So both your tests should work but it would be easier to do qs.joinPages(a,b,c)


If by another thread you mean a whole new thread to present the view, then I’d prefer to do that. But first, I’d like to remove every opinionated features in it before sharing it (I’m thinking about the file creation button that auto populate some of my own fields).
I don’t have much time right now to do that but when it will be done, I’ll share it properly.

But if you plan to mention it in another existing thread to help people, then sure, feel free to do it.

1 Like

Thanks for the explanation. Just tested and it’s working great! I’ll use the simpler joinPage(a,b,c) going forward.

I’m glad I asked. I won’t start a new thread and will wait for you to roll it out when you’re ready. In the mean time, if it comes up in conversation I’ll just point people to this thread.

And thanks again for all the help. This new music setup has improved my quality of life, I really appreciate that.

Cheers!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.