Skip to content

Instantly share code, notes, and snippets.

@mill1000
Last active January 11, 2025 22:50
Show Gist options
  • Select an option

  • Save mill1000/e07de721d3e76874db5c6bf5c6dda473 to your computer and use it in GitHub Desktop.

Select an option

Save mill1000/e07de721d3e76874db5c6bf5c6dda473 to your computer and use it in GitHub Desktop.
Custom Gerbera v1.12.1 Import Scripts
// NOTE: Scripts have not been updated for Gerbera 2.0+
// Helper to access first element of an array if it exists
function get(item, fallback) {
return (item && item[0]) || fallback;
}
// Custom audio import script
function addAudioCustom(obj) {
var title = get(obj.metaData[M_TITLE], obj.title);
var artist = get(obj.metaData[M_ARTIST], "Unknown");
var album = get(obj.metaData[M_ALBUM], "Unknown");
var date = get(obj.metaData[M_DATE]);
var decade = "Unknown";
if (/.*T.*/.test(date)) {
// Date appears to be a file modification time, ignore it
date = "Unknown"
}
else {
// Get year from full data
var year = getYear(date);
// Update metadata
obj.metaData[M_UPNP_DATE] = [year];
// Compute decade
decade = date.substring(0, 3) + "0s"
}
// Update object title
obj.title = title;
const chain = {
// Container for all artists
artists: { title: "Artists", objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
// Container for each artist
artist: { searchable: true, title: artist, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_ARTIST, metaData: {}, res: obj.res, aux: obj.aux, refID: obj.id },
// Container for all albums
albums: { title: "Albums", objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
// Container for each album
album: { searchable: true, title: album, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_ALBUM, metaData: {}, res: obj.res, aux: obj.aux, refID: obj.id },
// Root container for decades
decades: { title: "Decades", objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
// Container for each decade
decade: { title: decade, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
allTracks: { title: "All Tracks", objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
};
// Set metadata on the album container
chain.album.metaData[M_ARTIST] = [artist];
chain.album.metaData[M_CREATOR] = [artist];
chain.album.metaData[M_ALBUMARTIST] = [artist];
chain.album.metaData[M_DATE] = [date];
chain.album.metaData[M_ALBUM] = [album];
// Set metadata on the artist container
chain.artist.metaData[M_ARTIST] = [artist];
chain.artist.metaData[M_ALBUMARTIST] = [artist];
// Group by artist, then album
var container = addContainerTree([chain.artists, chain.artist, chain.album]);
addCdsObject(obj, container);
// Make the artist and album unsearchable so it only shows up once in results
chain.album.searchable = false;
chain.artist.searchable = false;
// Group by album
container = addContainerTree([chain.albums, chain.album]);
addCdsObject(obj, container);
// Group by decade, then album
container = addContainerTree([chain.decades, chain.decade, chain.album]);
addCdsObject(obj, container);
// All tracks
container = addContainerTree([chain.allTracks]);
addCdsObject(obj, container);
}
// NOTE: Scripts have not been updated for Gerbera 2.0+
// Custom Gerbera audio import script
if (getPlaylistType(orig.mimetype) === "") {
// Create a copy of the original object
var obj = orig;
// All virtual objects must reference an object in the PC-Directory
obj.refID = orig.id;
var mime = orig.mimetype.split("/")[0];
if (mime === "video" && obj.onlineservice === ONLINE_SERVICE_APPLE_TRAILERS) {
mime = "trailer";
} else if (orig.mimetype === "application/ogg") {
mime = (orig.theora === 1) ? "video" : "audio";
}
var audioLayout = config["/import/scripting/virtual-layout/attribute::audio-layout"] || "Default";
switch (mime) {
case "audio":
switch (audioLayout) {
case "Structured":
addAudioStructured(obj);
break;
case "Custom":
addAudioCustom(obj);
break;
default:
addAudio(obj);
break;
}
break;
default:
print("Ignored " + obj.location + " of type " + orig.mimetype);
break;
}
}
@TheDiscoPotato

Copy link
Copy Markdown

Hi there. I appreciate you posting your custom scripts. This is exactly what I was looking for. However, I'm having trouble implementing the audio_custom.js. I might be adding the script location incorrectly in the config.xml file. Would you possibly be able to share yours?

@mill1000

mill1000 commented Mar 8, 2022

Copy link
Copy Markdown
Author

This should be the relevant bit you need

<import hidden-files="no">
    <scripting script-charset="UTF-8">
      <common-script>/usr/local/share/gerbera/js/common.js</common-script>
      <playlist-script>/usr/local/share/gerbera/js/playlists.js</playlist-script>
      <custom-script>/home/rock/gerbera/gerbera-scripts/audio_custom.js</custom-script>
      <virtual-layout type="js" audio-layout="Custom">
        <import-script>/home/rock/gerbera/gerbera-scripts/import.js</import-script>
      </virtual-layout>
    </scripting>
  </import>

@TheDiscoPotato

TheDiscoPotato commented Mar 8, 2022

Copy link
Copy Markdown

Much appreciated. Looks like the trick was adding audio-layout="Custom". Cheers!

@thebream

thebream commented Mar 23, 2022

Copy link
Copy Markdown

Awesome example!

Just been playing with this, and found that to make the "searchable" attribute work as intended in the above audio_custom.js:

  // Make the artsit and album unsearchable so it only shows up once in results
  chain.album.searchable = false;
  chain.artist.searchable = false;

You need to add this to the SERVER section in your config.xml file
<upnp searchable-container-flag="yes" />

Otherwise, when you search for, say, an album called "Thriller" in your UPNP controller (e.g. BubbleUPnP), it will find it three times from:

  • Albums / Thriller
  • Artists / Michael Jackson / Thriller
  • Decades / 1980s / Thiller

@mill1000

Copy link
Copy Markdown
Author

That's correct. Good catch. Perhaps I should add a snippet of my config.xml for future reference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment