Last active
March 16, 2022 00:28
-
-
Save sk22/ad87ffebb6df2f2d5f181fb6830d9050 to your computer and use it in GitHub Desktop.
gerbera as an upnp-av/dlna server for web radio stations with nginx proxying https streams over http :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<config version="2" xmlns="http://mediatomb.cc/config/2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mediatomb.cc/config/2 http://mediatomb.cc/config/2.xsd"> | |
<!-- | |
See http://gerbera.io or read the docs for more | |
information on creating and using config.xml configuration files. | |
--> | |
<!-- ... --> | |
<import hidden-files="no"> | |
<scripting script-charset="UTF-8"> | |
<common-script>/usr/share/gerbera/js/common.js</common-script> | |
<playlist-script>/etc/gerbera/radio-playlists.js</playlist-script> | |
</scripting> | |
<!-- ... --> | |
</import> | |
<!-- ... --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 📻 modified by sk22 to use an nginx proxy for web radios | |
// (radio emoji indicates modified code) | |
var PROXY_PORT = 49180; // 📻 | |
// automatically generated output of `hostname -I` because nginx is running on localhost | |
var IP_ADDRESSES = '192.168.0.182 '; | |
// Default MediaTomb playlist parsing script. | |
// | |
// Note: This script currently only handles .m3u and .pls playlists, but it can | |
// easily be extended. It doesn't use the "correct" way to parse the playlist, | |
// but a more fault-tolerant way, so it will try to do it's best and won't | |
// complain even if the playlist is completely messed up. | |
/*MT_F* | |
MediaTomb - http://www.mediatomb.cc/ | |
playlists.js - this file is part of MediaTomb. | |
Copyright (C) 2006-2010 Gena Batyan <[email protected]>, | |
Sergey 'Jin' Bostandzhyan <[email protected]>, | |
Leonhard Wimmer <[email protected]> | |
This file is free software; the copyright owners give unlimited permission | |
to copy and/or redistribute it; with or without modifications, as long as | |
this notice is preserved. | |
This file 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. | |
$Id$ | |
*/ | |
/** | |
* 📻 | |
* @param {string} location stream url, e.g. https://stream.radio.com/live.mp3 | |
* @param {string} ip ip of proxy (on localhost), e.g. 192.168.0.182 | |
* @returns e.g. http://192.168.0.182/stream.radio.com/live.mp3 | |
*/ | |
function getProxiedLocation(location, ip) { | |
if (location.indexOf(https) == 0) { | |
return 'http://' + ip + ':' + PROXY_PORT | |
+ '/' + location.slice(https.length); | |
} else { | |
return location; | |
} | |
} | |
/** | |
* 📻 only using first ipv4 address as gerbera will only bind to one interface anyway | |
*/ | |
var ipAddress = IP_ADDRESSES.split(' ').filter(function (val) { | |
return val.indexOf('.') >= 0; | |
})[0]; | |
var playlistOrder = 1; | |
function addRadioPlaylistItem(location, title, playlistChain, order, proxied) { // 📻 | |
if (location.match(/^.*:\/\//)) { | |
var exturl = new Object(); | |
exturl.mimetype = 'audio/mpeg'; | |
exturl.objectType = OBJECT_TYPE_ITEM_EXTERNAL_URL; | |
exturl.location = proxied ? getProxiedLocation(location, ipAddress) : location; // 📻 | |
exturl.title = (title ? title : location); | |
exturl.protocol = 'http-get'; | |
exturl.upnpclass = UPNP_CLASS_ITEM_MUSIC_TRACK; | |
exturl.description = "Song from " + playlist.title; | |
exturl.playlistOrder = (order ? order : playlistOrder++); | |
exturl.meta = exturl.meta || new Array(); // 📻 | |
// 📻 setting artist to item name because live streams overwrite title | |
// (so we can have the song title in the title and the station name in | |
// the artist field) | |
exturl.meta[M_ARTIST] = exturl.meta[M_ARTIST] || exturl.title; // 📻 | |
print('add object ' + exturl.title + ' to chain ' + playlistChain) | |
addCdsObject(exturl, playlistChain, UPNP_CLASS_PLAYLIST_CONTAINER); | |
} | |
else { | |
if (location.substr(0, 1) != '/') | |
location = playlistLocation + location; | |
var item = new Object(); | |
item.location = location; | |
if (title) | |
item.title = title; | |
else { | |
var locationParts = location.split('/'); | |
item.title = locationParts[locationParts.length - 1]; | |
if (!item.title) | |
item.title = location; | |
} | |
item.objectType = OBJECT_TYPE_ITEM; | |
item.playlistOrder = (order ? order : playlistOrder++); | |
addCdsObject(item, playlistChain, UPNP_CLASS_PLAYLIST_CONTAINER); | |
} | |
} | |
print("Processing playlist: " + playlist.location); | |
var playlistLocation = playlist.location.substring(0, playlist.location.lastIndexOf('/') + 1); | |
// the function getPlaylistType is defined in common.js | |
var type = getPlaylistType(playlist.mimetype); | |
// the function createContainerChain is defined in common.js | |
var playlist_title = playlist.title | |
var dot_index = playlist_title.lastIndexOf('.'); | |
if (dot_index > 1) | |
playlist_title = playlist_title.substring(0, dot_index); | |
var playlistChainProxied = createContainerChain(new Array(playlist_title)); // 📻 | |
var playlistChainDirect = createContainerChain(new Array('direct', playlist_title)); // 📻 | |
var playlistDirChain = null; // 📻 not using the directory structure chain | |
// 📻 var last_path = getLastPath(playlist.location); | |
// if (last_path) | |
// playlistDirChain = createContainerChain(new Array('Playlists', 'Directories', last_path, playlist_title)); | |
if (type == '') { | |
print("Unknown playlist mimetype: '" + playlist.mimetype + "' of playlist '" + playlist.location + "'"); | |
} | |
else if (type == 'm3u') { | |
var title = null; | |
var line = readln(); | |
do { | |
if (line.match(/^#EXTINF:(-?\d+),(\S.+)$/i)) { | |
// duration = RegExp.$1; // currently unused | |
title = line.split(',')[1] | |
} | |
else if (!line.match(/^(#|\s*$)/)) { | |
addRadioPlaylistItem(line, title, playlistChainProxied, null, true); // 📻 | |
addRadioPlaylistItem(line, title, playlistChainDirect, null, false); // 📻 | |
if (playlistDirChain) | |
addRadioPlaylistItem(line, title, playlistDirChain); | |
title = null; | |
} | |
line = readln(); | |
} | |
while (line); | |
} | |
else if (type == 'pls') { | |
/* 📻 only using m3u for radio stations at the moment | |
var title = null; | |
var file = null; | |
var lastId = -1; | |
var line = readln(); | |
do { | |
if (line.match(/^\[playlist\]$/i)) { | |
// It seems to be a correct playlist, but we will try to parse it | |
// anyway even if this header is missing, so do nothing. | |
} | |
else if (line.match(/^NumberOfEntries=(\d+)$/i)) { | |
// var numEntries = RegExp.$1; | |
} | |
else if (line.match(/^Version=(\d+)$/i)) { | |
// var plsVersion = RegExp.$1; | |
} | |
else if (line.match(/^File\s*(\d+)\s*=\s*(\S.+)$/i)) { | |
var thisFile = RegExp.$2; | |
var id = parseInt(RegExp.$1, 10); | |
if (lastId == -1) | |
lastId = id; | |
if (lastId != id) { | |
if (file) { | |
addRadioPlaylistItem(file, title, playlistChain, lastId); | |
if (playlistDirChain) | |
addRadioPlaylistItem(file, title, playlistDirChain, lastId); | |
} | |
title = null; | |
lastId = id; | |
} | |
file = thisFile | |
} | |
else if (line.match(/^Title\s*(\d+)\s*=\s*(\S.+)$/i)) { | |
var thisTitle = RegExp.$2; | |
var id = parseInt(RegExp.$1, 10); | |
if (lastId == -1) | |
lastId = id; | |
if (lastId != id) { | |
if (file) { | |
addRadioPlaylistItem(file, title, playlistChain, lastId); | |
if (playlistDirChain) | |
addRadioPlaylistItem(file, title, playlistDirChain, lastId); | |
} | |
file = null; | |
lastId = id; | |
} | |
title = thisTitle; | |
} | |
else if (line.match(/^Length\s*(\d+)\s*=\s*(\S.+)$/i)) { | |
// currently unused | |
} | |
line = readln(); | |
} | |
while (line); | |
if (file) { | |
addRadioPlaylistItem(file, title, playlistChain, lastId); | |
if (playlistDirChain) | |
addRadioPlaylistItem(file, title, playlistDirChain, lastId); | |
} | |
*/ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# only needed if using dynamic ip | |
# to be run before gerbera starts; hard codes the current ip addresses into the radio-playlists.js script | |
sed "s/IP_ADDRESSES =.*$/IP_ADDRESSES = '`hostname -I`';/" -i /etc/gerbera/radio-playlists.js | |
echo updated ip addresses to `hostname -I` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# proxies https://stream.radio.com:8001/live.mp3 | |
# via http://0.0.0.0:8081/stream.radio.com:8001/live.mp3 | |
# ...so your (my) old web radio can play the stream via http :) | |
map $upstream_status $new_location { | |
301 http://$host:49180/$upstream_http_location; | |
default ''; | |
} | |
server { | |
listen 49180; | |
resolver 1.1.1.1; | |
merge_slashes off; | |
location ~ ^/https://(.*)$ { | |
return 302 /$1; | |
} | |
location ~ ^/(.*)$ { | |
proxy_pass https://$1; | |
proxy_set_header "User-Agent" "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:98.0) Gecko/20100101 Firefox/98.0"; | |
proxy_hide_header Location; | |
add_header Location $new_location; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# only needed if using dynamic ip address! | |
# for fixed ip addresses, you can keep using the original systemd service | |
# first stop & disable original gerbera systemd service: | |
# $ sudo systemctl stop gerbera.service | |
# $ sudo systemctl disable gerbera.service | |
# then enable & start this custom gerbera systemd service: | |
# $ sudo systemctl enable gerbera-custom.service | |
# $ sudo systemctl start gerbera-custom.service | |
[Unit] | |
Description=Gerbera Media Server | |
Documentation=man:gerbera(1) https://gerbera.io/ | |
After=mysql.target network.target | |
[Service] | |
Type=simple | |
User=gerbera | |
Group=gerbera | |
ExecStartPre=/etc/gerbera/update-gerbera-ip.sh | |
ExecStart=/usr/bin/gerbera -c /etc/gerbera/config.xml | |
Restart=on-failure | |
RestartSec=5 | |
[Install] | |
WantedBy=multi-user.target |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment