Jun 062020
 

I was looking for a solution to play video on my vr headset ($3) Samsung 8 in some viewer, aka the cardboard vr. First I thought that I would just view them over the network, but my rooted chosen ROM (Linage 17) doesn’t support smb from boot, so no fstab mapping can be done leaving me with a hacky file browse and view by another app solution.

Then by chance playing with my TV and trying to watch films on the computer I finally found a use for a UPNP/DLNA Server. I also found that in my VR apps, any form of networking found was for a uPNP/DLNA server, not smb logging on etc, sweet! Looking at the Windows uPNP offering I found none, for free or paid for, they could not make a custom menu from meta data tags.

So Linux… Gerbera free and claimed to be able to do this so I had a go. A bit of hack trial and error later I got it going how I wanted with a sort of 2 level keyword search and reading all the tags I need to sort the collection. I meta tagged all my MP4 video with Mp3Tag v3.01, which is excellent util for the meta tagging via many different ways and easily expandable due to a simple text based plugin type feature.

Gerbera Custom Menu

Gerbera Custom Menu

I leave it here so maybe it’ll help someone else. config.xml, notably.


<library-options>
<ffmpeg>
<auxdata>
<add-data tag="grouping"/>
<add-data tag="rating"/>
<add-data tag="organization"/>
</auxdata>
</ffmpeg>
<id3>
<auxdata>
</auxdata>
</id3>
</library-options>

The import script. Function addVideo(obj) is when the only modified function and does all the heavy lifting for rhe menu.


/*GRB*
Gerbera - https://gerbera.io/
import.js - this file is part of Gerbera.
Copyright (C) 2018 Gerbera Contributors
Gerbera is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
Gerbera is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Gerbera.  If not, see <http://www.gnu.org/licenses/>.
$Id$
*/
function addAudio(obj) {
var desc = '';
var artist_full;
var album_full;
// first gather data
var title = obj.meta[M_TITLE];
if (!title) {
title = obj.title;
}
var artist = obj.meta[M_ARTIST];
if (!artist) {
artist = 'Unknown';
artist_full = null;
} else {
artist_full = artist;
desc = artist;
}
var album = obj.meta[M_ALBUM];
if (!album) {
album = 'Unknown';
album_full = null;
} else {
desc = desc + ', ' + album;
album_full = album;
}
if (desc) {
desc = desc + ', ';
}
desc = desc + title;
var date = obj.meta[M_DATE];
if (!date) {
date = 'Unknown';
} else {
date = getYear(date);
obj.meta[M_UPNP_DATE] = date;
desc = desc + ', ' + date;
}
var genre = obj.meta[M_GENRE];
if (!genre) {
genre = 'Unknown';
} else {
desc = desc + ', ' + genre;
}
var description = obj.meta[M_DESCRIPTION];
if (!description) {
obj.description = desc;
}
var composer = obj.meta[M_COMPOSER];
if (!composer) {
composer = 'None';
}
var conductor = obj.meta[M_CONDUCTOR];
if (!conductor) {
conductor = 'None';
}
var orchestra = obj.meta[M_ORCHESTRA];
if (!orchestra) {
orchestra = 'None';
}
// uncomment this if you want to have track numbers in front of the title
// in album view
/*
var track = obj.meta[M_TRACKNUMBER];
if (!track) {
track = '';
} else {
if (track.length == 1) {
track = '0' + track;
}
track = track + ' ';
}
*/
// comment the following line out if you uncomment the stuff above  :)
var track = '';
// uncomment this if you want to have channel numbers in front of the title
/*
var channels = obj.res[R_NRAUDIOCHANNELS];
if (channels) {
if (channels === "1") {
track = track + '[MONO]';
} else if (channels === "2") {
track = track + '[STEREO]';
} else {
track = track + '[MULTI]';
}
}
*/
var chain = ['Audio', 'All Audio'];
obj.title = title;
addCdsObject(obj, createContainerChain(chain));
chain = ['Audio', 'Artists', artist, 'All Songs'];
addCdsObject(obj, createContainerChain(chain));
chain = ['Audio', 'All - full name'];
var temp = '';
if (artist_full) {
temp = artist_full;
}
if (album_full) {
temp = temp + ' - ' + album_full + ' - ';
} else {
temp = temp + ' - ';
}
obj.title = temp + title;
addCdsObject(obj, createContainerChain(chain));
chain = ['Audio', 'Artists', artist, 'All - full name'];
addCdsObject(obj, createContainerChain(chain));
chain = ['Audio', 'Artists', artist, album];
obj.title = track + title;
addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);
chain = ['Audio', 'Albums', album];
obj.title = track + title;
addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_ALBUM);
chain = ['Audio', 'Genres', genre];
addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_GENRE);
chain = ['Audio', 'Year', date];
addCdsObject(obj, createContainerChain(chain));
chain = ['Audio', 'Composers', composer];
addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER_MUSIC_COMPOSER);
}
function addVideo(obj) {
// first gather data
obj.filename=obj.title;
var title = obj.meta[M_TITLE];
if (!title) {
title = obj.title;
}
var genre = obj.meta[M_GENRE];
if (!genre) {
genre = 'No Genre';
}
var grouping = obj.aux['grouping'];
if (grouping) {
grouping=grouping.toLowerCase();
title = grouping.split(' ')[0] + ' ' + title;
} else {
var match = null;
var reg =  {0:/(?:^|[ _])(\d{3,4}P[ _]\d{3,4}K)/i,
1:/(?:^|[ _])(\d{3,4}P)[ _\.]/i};
for(var a;a<reg.length;a++) {
match = obj.filename.match(reg[a]);
if (match!==null)
break;
}
if (match!==null)
grouping = match.toLowerCase();
else
grouping = 'No Resolution';
}
//var duration = '00:05:00.5';
var duration = obj.res['duration'];
duration = "2000-01-01T"+duration;
duration = new Date(duration);
var hours = duration.getTimezoneOffset()/60;
hours = duration.getHours() + hours;
duration =( hours * 60) + duration.getMinutes();
//print(hours);
switch(true)
{
case duration < 5:
title = '<05' + ' ' + title;
duration = "< 05";
break;
case duration < 10:
title = '<10' + ' ' + title;
duration = "< 10";
break;
case duration < 30:
title = '<30' + ' ' + title;
duration = "< 30";
break;
case duration < 60:
title = '<60' + ' ' + title;
duration = "< 60"; break; case duration > 60:
title = '>60' + ' ' + title;
duration = "> 60";
break;
}
var rating = obj.aux['rating'];
if (rating == null) {
rating = 'NR';
} else if(!isNaN(Number(rating))) {
rating='*'.repeat(rating);
}
title = rating + ' ' + title;
var artist = obj.meta[M_ARTIST];
if (artist) {
title = artist + ' ' + title;
} else {
artist = 'No Artist';
}
var orientation = obj.aux['organization'];
if (orientation=='TB') {
title = 'TB ' + title;
} else if(orientation==null) {
orientation='?';
}
var chain = ['Video', '@ All Video'];
obj.title = title;
addCdsObject(obj, createContainerChain(chain));
var ignore = ['180° in Virtual Reality','3D in Virtual Reality','Virtual Reality','Closed Captions'];
var rename = ['POV in Virtual Reality','POV','Popular With Women','Girl Porn','Voyeur in Virtual Reality','Voyeur','2D in Virtual Reality','2D','360° in Virtual Reality','360°','Double Penetration','DP'];
var description = obj.meta[M_DESCRIPTION];
if (description)
{
var des_spt = description.split(',');
for(a=0;a<des_spt.length;a++)
des_spt[a]=des_spt[a].trim();
for(a=0;a<ignore.length;a++) for (b=des_spt.length-1; b>=0; b--)
if (des_spt[b] == ignore[a])
des_spt.splice(b, 1);
for(a=0;a<rename.length;a=a+2)
for(b=0;b<des_spt.length;b++)
if(des_spt[b]==rename[a])
des_spt[b]=rename[a+1];
for (a=0;a<des_spt.length;a++)
{
obj.keywordA = des_spt[a];
chain = ['Video', 'Keyword', obj.keywordA];
addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
//chain = ['Video', 'Keyword', obj.keywordA, '$ All'];
//addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
for (b=0;b<des_spt.length;b++) { if (des_spt[a] == des_spt[b]) continue; obj.keywordB = des_spt[b]; chain = ['Video', 'Keyword', obj.keywordA, obj.keywordB]; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER); } } } //------------------CHAINS chain = ['Video', 'Actors', artist]; obj.artist = artist; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER); chain = ['Video', 'Genre', genre]; obj.genre = genre; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER); chain = ['Video', 'Resolution', grouping]; obj.grouping = grouping; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER); chain = ['Video', 'Duration', duration]; obj.duration = duration; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER); chain = ['Video', 'Rating', rating]; obj.rating = rating; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER); chain = ['Video', 'Orientation', orientation]; obj.orientation = orientation; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER); var dir = getRootPath(object_script_path, obj.location); if (dir.length > 0) {
chain = ['Video', '@ Directories'];
chain = chain.concat(dir);
addCdsObject(obj, createContainerChain(chain));
}
//print(JSON.stringify(obj));
}
/*
{
"objectType":2,
"id":14501,
"parentID":14372,
"title":"1G <60 2160p Cute little spinner does anal VR",
"upnpclass":"object.item.videoItem",
"location":"/media/VRNorp/1G/Anal/sm VR SBS big dick 1g v and a - 2160p 8000k 215335782.mp4",
"mtime":1591102146,
"sizeOnDisk":-1667547768,
"restricted":1,
"theora":0,
"onlineservice":0,
"meta":{
"dc:title":"Cute little spinner does anal VR",
"upnp:artist":"1G",
"upnp:genre":"A and V",
"dc:description":"180° in Virtual Reality,3D in Virtual Reality,60FPS,Anal,Blowjob,Czech,HD Porn,Pornstar,Red Head,Small Tits,Virtual Reality"
},
"aux":{
"description":"180° in Virtual Reality,3D in Virtual Reality,60FPS,Anal,Blowjob,Czech,HD Porn,Pornstar,Red Head,Small Tits,Virtual Reality",
"grouping":"2160p 8000k",
"Rating":"1"
},
"res":{
"protocolInfo":"http-get:*:video/mp4:*",
"size":"2627419528",
"bitrate":"1015771",
"duration":"00:43:06.6",
"sampleFrequency":"48000",
"nrAudioChannels":"2",
"resolution":"4320x2160"
},
"mimetype":"video/mp4",
"refID":14501,
"keyword":"Virtual Reality",
"artist":"1G",
"genre":"A and V",
"grouping":"2160p 8000k",
"duration":"< 60" } */ function addImage(obj) { var chain = ['Photos', 'All Photos']; addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER); var date = obj.meta[M_DATE]; if (date) { var dateParts = date.split('-'); if (dateParts.length > 1) {
var year = dateParts[0];
var month = dateParts[1];
chain = ['Photos', 'Year', year, month];
addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
}
chain = ['Photos', 'Date', date];
addCdsObject(obj, createContainerChain(chain), UPNP_CLASS_CONTAINER);
}
var dir = getRootPath(object_script_path, obj.location);
if (dir.length > 0) {
chain = ['Photos', 'Directories'];
chain = chain.concat(dir);
addCdsObject(obj, createContainerChain(chain));
}
}
function addTrailer(obj) {
var chain;
chain = ['Online Services', 'Apple Trailers', 'All Trailers'];
addCdsObject(obj, createContainerChain(chain));
var genre = obj.meta[M_GENRE];
if (genre) {
genres = genre.split(', ');
for (var i = 0; i < genres.length; i++) { chain = ['Online Services', 'Apple Trailers', 'Genres', genres[i]]; addCdsObject(obj, createContainerChain(chain)); } } var reldate = obj.meta[M_DATE]; if ((reldate) && (reldate.length >= 7)) {
chain = ['Online Services', 'Apple Trailers', 'Release Date', reldate.slice(0, 7)];
addCdsObject(obj, createContainerChain(chain));
}
var postdate = obj.aux[APPLE_TRAILERS_AUXDATA_POST_DATE];
if ((postdate) && (postdate.length >= 7)) {
chain = ['Online Services', 'Apple Trailers', 'Post Date', postdate.slice(0, 7)];
addCdsObject(obj, createContainerChain(chain));
}
}
// main script part
if (getPlaylistType(orig.mimetype) === '') {
var arr = orig.mimetype.split('/');
var mime = arr[0];
// var obj = copyObject(orig);
var obj = orig;
obj.refID = orig.id;
if (mime === 'audio') {
addAudio(obj);
}
if (mime === 'video') {
if (obj.onlineservice === ONLINE_SERVICE_APPLE_TRAILERS) {
addTrailer(obj);
} else {
addVideo(obj);
}
}
if (mime === 'image') {
addImage(obj);
}
if (orig.mimetype === 'application/ogg') {
if (orig.theora === 1) {
addVideo(obj);
} else {
addAudio(obj);
}
}
}
Gerbera Custom Menu 2

Gerbera Custom Menu 2

Share

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>