import enigma2 -> mediatomb radio sync script
authorChristian Weiske <cweiske@cweiske.de>
Tue, 20 May 2008 20:57:49 +0000 (20:57 +0000)
committerChristian Weiske <cweiske@cweiske.de>
Tue, 20 May 2008 20:57:49 +0000 (20:57 +0000)
ABOUT [new file with mode: 0644]
DreamboxBouqets.php [new file with mode: 0644]
config.php.dist [new file with mode: 0644]
copy-radio-bouqets.php [new file with mode: 0644]
sync-radiobouqets.php [new file with mode: 0644]
syncfunctions.php [new file with mode: 0644]

diff --git a/ABOUT b/ABOUT
new file mode 100644 (file)
index 0000000..34bad9c
--- /dev/null
+++ b/ABOUT
@@ -0,0 +1 @@
+enigma2 radio stations to mediatomb converter
diff --git a/DreamboxBouqets.php b/DreamboxBouqets.php
new file mode 100644 (file)
index 0000000..2e420d5
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+* Fetches bouqet information from the dreambox
+*
+* @author Christian Weiske <cweiske@cweiske.de>
+*/
+class DreamboxBouqets
+{
+    /**
+    * Fetch radio bouqets
+    *
+    * @param string $ip IP-address or hostname of the dreambox
+    *
+    * @return array Array of bouqets and the items
+    */
+    public static function fetchRadioBouqets($ip)
+    {
+        $host       = 'http://' . $ip;
+        $streamhost = 'http://' . $ip . ':8001';
+        $url_getServices     = $host . "/web/getservices?sRef=";
+        $url_stream_playlist = $host . '/web/stream.m3u?ref=';
+        //1%3A7%3A2%3A0%3A0%3A0%3A0%3A0%3A0%3A0%3A(type%20%3D%3D%202)%20FROM%20BOUQUET%20%22userbouquet.info_______________radio_.radio%22%20ORDER%20BY%20bouquet
+        $url_bouqet_epg      = $host . '/web/epgnow?bRef=';
+        $url_stream          = $streamhost . '/';
+
+        //TODO: fetch from dreambox config.js
+        $bouqet_radio = '1:7:2:0:0:0:0:0:0:0:(type == 2)FROM BOUQUET "bouquets.radio" ORDER BY bouquet';
+
+
+        $url = $url_getServices . urlencode($bouqet_radio);
+        $radiobouqets = new SimpleXMLElement(
+            file_get_contents($url)
+        );
+
+        $arBouqets = array();
+        foreach ($radiobouqets->e2service as $bouqet) {
+            $strBouqetName = self::properBouqetName($bouqet->e2servicename);
+            $arBouqets[$strBouqetName] = array();
+
+            $strRef = $bouqet->e2servicereference;
+            $url    = $url_getServices . urlencode($strRef);
+            $epgurl = $url_bouqet_epg  . urlencode($strRef);
+
+            $stations = new SimpleXMLElement(
+                file_get_contents(
+                    $url
+                )
+            );
+            foreach ($stations->e2service as $station) {
+                $strStationRef = (string)$station->e2servicereference;
+                $arBouqets[$strBouqetName][$strStationRef] = array(
+                    'name'     => (string)$station->e2servicename,
+                    'playlist' => $url_stream_playlist . $strStationRef,
+                    'stream'   => $url_stream . $strStationRef
+                );
+            }
+
+            $epgs = new SimpleXMLElement(
+                file_get_contents($epgurl)
+            );
+            foreach ($epgs->e2event as $epg) {
+                $strStationRef = (string)$epg->e2eventservicereference;
+                $arBouqets[$strBouqetName][$strStationRef] = array_merge(
+                    $arBouqets[$strBouqetName][$strStationRef],
+                    array(
+                        'epgtitle'        => (string)$epg->e2eventtitle,
+                        'epgdescription'  => (string)$epg->e2eventdescription,
+                        'epgdescription2' => (string)$epg->e2eventdescriptionextendet,
+                    )
+                );
+            }
+
+        }
+
+        return $arBouqets;
+    }//public static function fetchRadioBouqets(..)
+
+
+
+    protected static function properBouqetName($name)
+    {
+        return trim(preg_replace('#\(.+\)$#', '', $name));
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/config.php.dist b/config.php.dist
new file mode 100644 (file)
index 0000000..e5036fc
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+$GLOBALS['enigtombConfig'] = array(
+    'dreambox' => array(
+        'host' => 'dreambox'
+    ),
+    'mediatomb' => array(
+        'host' => 'dojo',
+        'port' => 49152,
+        'user' => 'cweiske',
+        'pass' => 'mediatomb',
+
+        'radiodir'   => 'Radio',
+        'rootBouqet' => 'Favourites',
+        'mimetype'   => 'audio/x-satellite-mpeg2'
+    ),
+    'namefixes' => array(
+        'DKULTUR'      => 'D-Kultur',
+        'DLF'          => 'Deutschlandfunk',
+        'JAZZ RADIO'   => 'Jazz Radio',
+        'MDR FIGARO'   => 'Figaro',
+        'MDR INFO'     => 'mdr info',
+        'MDR JUMP'     => 'Jump',
+        'MDR SPUTNIK'  => 'Sputnik',
+        'MDR1 SACHSEN' => 'mdr1 Radio Sachsen',
+        'P-COUNTRY'    => 'Premiere Country',
+        'VIBRATION'    => 'Vibration',
+    )
+);
+?>
\ No newline at end of file
diff --git a/copy-radio-bouqets.php b/copy-radio-bouqets.php
new file mode 100644 (file)
index 0000000..6d7bf3b
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+* Copies all radio bouqets into a folder on mediatomb.
+* Deletes/clears the folder at first.
+*
+* @author   Christian Weiske <cweiske@php.net>
+* @license  LGPL http://www.gnu.org/copyleft/lesser.html
+*/
+require_once 'DreamboxBouqets.php';
+require_once 'Services/MediaTomb.php';
+require_once 'config.php';
+
+echo "fetching bouqets\n";
+$arBouqets = DreamboxBouqets::fetchRadioBouqets(
+    $GLOBALS['enigtombConfig']['dreambox']['host']
+);
+
+$mt = new Services_MediaTomb(
+    $GLOBALS['enigtombConfig']['mediatomb']['user'],
+    $GLOBALS['enigtombConfig']['mediatomb']['pass'],
+    $GLOBALS['enigtombConfig']['mediatomb']['host'],
+    $GLOBALS['enigtombConfig']['mediatomb']['port']
+);
+
+$strRadiodir = $GLOBALS['enigtombConfig']['mediatomb']['radiodir'];
+
+$radiodir = $mt->getContainerByPath($strRadiodir);
+if ($radiodir !== null) {
+    $mt->delete($radiodir);
+}
+
+foreach ($arBouqets as $strBouqetName => $arStations) {
+    echo "adding " . $strBouqetName . "\n";
+    if ($strBouqetName == $GLOBALS['enigtombConfig']['mediatomb']['rootBouqet']) {
+        $dir = $mt->getContainerByPath($strRadiodir);
+        if ($dir === null) {
+            $dir = $mt->createContainerByPath($strRadiodir);
+        }
+    } else {
+        $dir = $mt->createContainerByPath($strRadiodir . '/' . $strBouqetName);
+    }
+
+    foreach ($arStations as $arStation) {
+        echo " adding " . $arStation['name'] . "\n";
+        $strName = $arStation['name'];
+        if ($arStation['epgtitle']) {
+            $strName .= ' - ' . $arStation['epgtitle'];
+        }
+        if ($arStation['epgdescription']) {
+            $strDescription = $arStation['epgdescription'];
+        } else {
+            $strDescription = '';
+        }
+        if ($arStation['epgdescription2']) {
+            $strDescription .= ' ' . $arStation['epgdescription2'];
+        }
+        if (!$strDescription) {
+            $strDescription = $strName;
+        }
+        $mt->createExternalLink(
+            $dir->id, $strName, $arStation['stream'], trim($strDescription),
+            $GLOBALS['enigtombConfig']['mediatomb']['mimetype'],
+            Services_MediaTomb::PROTOCOL_HTTP_GET
+        );
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/sync-radiobouqets.php b/sync-radiobouqets.php
new file mode 100644 (file)
index 0000000..1af0868
--- /dev/null
@@ -0,0 +1,165 @@
+<?php
+/**
+* Syncs all dreambox radio bouqets into a folder on mediatomb.
+* Renames/changes existing entries instead of deleting
+* everything first.
+*
+* @author   Christian Weiske <cweiske@php.net>
+* @license  LGPL http://www.gnu.org/copyleft/lesser.html
+*/
+require_once 'DreamboxBouqets.php';
+require_once 'Services/MediaTomb.php';
+require_once 'config.php';
+require_once dirname(__FILE__) . '/syncfunctions.php';
+
+$bReally = true;
+
+echo "Synchronizing dreambox radio bouqets and mediatomb radion station list\n";
+echo " fetching bouqets\n";
+$arBouqets = DreamboxBouqets::fetchRadioBouqets(
+    $GLOBALS['enigtombConfig']['dreambox']['host']
+);
+
+$mt = new Services_MediaTomb(
+    $GLOBALS['enigtombConfig']['mediatomb']['user'],
+    $GLOBALS['enigtombConfig']['mediatomb']['pass'],
+    $GLOBALS['enigtombConfig']['mediatomb']['host'],
+    $GLOBALS['enigtombConfig']['mediatomb']['port']
+);
+
+$strRadiodir = $GLOBALS['enigtombConfig']['mediatomb']['radiodir'];
+
+$radiodir = $mt->createContainerByPath($strRadiodir);
+
+/**
+* create bouqet/container sync list
+*/
+$arBouqetNames        = array_keys($arBouqets);
+$arRadiodirContainers = $radiodir->getContainers();
+$arContainerNames     = array();
+foreach ($arRadiodirContainers as $nId => $container) {
+    $arBouqetNames[] = $container->title;
+    $arContainerNames[$container->title] = $nId;
+}
+
+if (isset($GLOBALS['enigtombConfig']['mediatomb']['rootBouqet'])
+    && $GLOBALS['enigtombConfig']['mediatomb']['rootBouqet']
+) {
+    $arBouqetNames[] = $GLOBALS['enigtombConfig']['mediatomb']['rootBouqet'];
+    $arContainerNames[$GLOBALS['enigtombConfig']['mediatomb']['rootBouqet']] = $radiodir->id;
+    $arRadiodirContainers[$radiodir->id] = $radiodir;
+}
+
+$arBouqetNames = array_unique($arBouqetNames);
+
+/**
+* sync bouqets and containers
+* Does not know about renamed bouqets
+*/
+echo " syncing bouqets and containers\n";
+$arValidContainers = array();
+foreach ($arBouqetNames as $strBouqetName) {
+    if (isset($arContainerNames[$strBouqetName]) && isset($arBouqets[$strBouqetName])) {
+        //everything is ok
+        echo "  " . $strBouqetName . " already exists, ok\n";
+        $nContainerId = $arContainerNames[$strBouqetName];
+        $arValidContainers[$nContainerId] = $arRadiodirContainers[$nContainerId];
+    } else if (!isset($arContainerNames[$strBouqetName])) {
+        //container does not exist
+        if ($strBouqetName != $GLOBALS['enigtombConfig']['mediatomb']['rootBouqet']) {
+            echo "  creating " . $strBouqetName . "\n";
+            $container = $mt->createContainerByPath($strRadiodir . '/' . $strBouqetName);
+            $arValidContainers[$container->id] = $container;
+        }
+    } else if (!isset($arBouqets[$strBouqetName])) {
+        if ($strBouqetName != $GLOBALS['enigtombConfig']['mediatomb']['rootBouqet']) {
+            //bouqet does not exist anymore -> delete container
+            echo "  deleting container " . $strBouqetName . "\n";
+            if ($bReally) {
+                $mt->delete($arContainerNames[$strBouqetName]);
+            }
+        }
+    }
+}
+
+/**
+* Sync radio stations
+*/
+echo " syncing stations\n";
+foreach ($arValidContainers as $nContainerId => $container) {
+    /**
+    * Build stations sync list
+    */
+    if (isset($GLOBALS['enigtombConfig']['mediatomb']['rootBouqet'])
+        && $GLOBALS['enigtombConfig']['mediatomb']['rootBouqet']
+        && $container == $radiodir
+    ) {
+        $strContainerTitle = $GLOBALS['enigtombConfig']['mediatomb']['rootBouqet'];
+    } else {
+        $strContainerTitle = $container->title;
+    }
+    echo "  syncing stations in " . $strContainerTitle . "\n";
+
+    //existing stations in mediatomb
+    $arStationItems      = $container->getItems(0, 1000);
+    $arStationUris       = array();
+    $arStationItemsByUri = array();
+    foreach ($arStationItems as $item) {
+        $arStationUris[]                 = $item->url;
+        $arStationItemsByUri[$item->url] = $item;
+    }
+    unset($arStationItems);
+
+    //stations in bouqet
+    foreach ($arBouqets[$strContainerTitle] as $arStation) {
+        $arStationUris[]                             = $arStation['stream'];
+        $arBouqetStationsByUri[$arStation['stream']] = $arStation;
+    }
+    unset($arBouqets[$strContainerTitle]);
+
+    $arStationUris = array_unique($arStationUris);
+
+    //sync stations
+    foreach ($arStationUris as $strUri) {
+        echo '   syncing station ' . $strUri . "\n";
+        if (isset($arBouqetStationsByUri[$strUri]) && isset($arStationItemsByUri[$strUri])) {
+            //station exists on both dreambox and mediatomb
+            //update title
+            echo "    updating title and description\n";
+            $arTitles = enigtombGetStationTitle($arBouqetStationsByUri[$strUri]);
+            echo "    " . $arTitles['title'] . "\n";
+            if ($arTitles['description'] && $arTitles['description'] != $arTitles['title']) {
+                echo "    " . $arTitles['description'] . "\n";
+            }
+            $arStationItemsByUri[$strUri]->title       = $arTitles['title'];
+            $arStationItemsByUri[$strUri]->description = $arTitles['description'];
+            if ($bReally) {
+                $arStationItemsByUri[$strUri]->save();
+            }
+        } else if (!isset($arBouqetStationsByUri[$strUri])) {
+            //station does not exist on dreambox
+            //delete it in mediatomb
+            echo "    deleting\n";
+            if ($bReally) {
+                $arStationItemsByUri[$strUri]->delete();
+            }
+        } else if (!isset($arStationItemsByUri[$strUri])) {
+            //station exists on dreambox only
+            //create it in mediatomb
+            echo "    creating\n";
+            $arTitles = enigtombGetStationTitle($arBouqetStationsByUri[$strUri]);
+            echo "    " . $arTitles['title'] . "\n";
+            if ($bReally) {
+                $arStationItemsByUri[$strUri]->createExternalLink(
+                    $arTitles['title'], $strUri, $arTitles['description'],
+                    $GLOBALS['enigtombConfig']['mediatomb']['mimetype'],
+                    'http-get', 'object.item', false
+                );
+            }
+        }
+    }
+}
+
+
+echo "done\n";
+?>
\ No newline at end of file
diff --git a/syncfunctions.php b/syncfunctions.php
new file mode 100644 (file)
index 0000000..a63d8a0
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+* Functions used in syncing
+*
+* @author Christian Weiske <cweiske@cweiske.de>
+* @license  LGPL http://www.gnu.org/copyleft/lesser.html
+*/
+
+if (!function_exists('enigtombGetStationTitle')) {
+    /**
+    * Returns a proper title from the bouqet station data
+    * for the mediatomb entry
+    *
+    * @param array $arStation Station in a dreambox bouqet
+    *
+    * @return array Array of title and description to use in the UPnP server.
+    *               Keys title and description
+    */
+    function enigtombGetStationTitle($arStation)
+    {
+        $strName = enigtombFixName($arStation['name']);
+
+        if ($arStation['epgtitle']
+            && $arStation['epgtitle'] != 'None'
+            && $arStation['epgtitle'] != $arStation['name']
+        ) {
+            if (strtolower(substr($arStation['epgtitle'], 0, strlen($strName))) == strtolower($strName)) {
+                $strEpgTitle = substr($arStation['epgtitle'], strlen($strName));
+            } else if (strtolower(substr($arStation['epgtitle'], 0, strlen($arStation['name']))) == strtolower($arStation['name'])) {
+                $strEpgTitle = substr($arStation['epgtitle'], strlen($arStation['name']));
+            } else {
+                $strEpgTitle = $arStation['epgtitle'];
+            }
+            $strEpgTitle = trim($strEpgTitle);
+            if (substr($strEpgTitle, 0, 1) == '-') {
+                $strEpgTitle = trim(substr($strEpgTitle, 1));
+            }
+
+            if (strtoupper($strEpgTitle) == $strEpgTitle) {
+                $strEpgTitle = ucwords(strtolower($strEpgTitle));
+            }
+
+            $strName .= ' - ' . $strEpgTitle;
+        }
+
+        if ($arStation['epgdescription'] && $arStation['epgdescription'] != 'None') {
+            $strDescription = $arStation['epgdescription'];
+        } else {
+            $strDescription = '';
+        }
+        if ($arStation['epgdescription2'] && $arStation['epgdescription2'] != 'None') {
+            $strDescription .= ' ' . $arStation['epgdescription2'];
+        }
+        if (!$strDescription) {
+            $strDescription = $strName;
+        }
+
+        return array(
+            'title'       => trim($strName),
+            'description' => trim($strDescription)
+        );
+    }//function enigtombGetStationTitle(..)
+}
+
+
+
+if (!function_exists('enigtombFixName')) {
+    /**
+    * Replaces the given name with a fixed one from the config.
+    * Necessary, because some stations send their names in ALL UPPERCASE
+    * which just isn't nicely readable.
+    *
+    * @param string $strName Name to fix
+    *
+    * @return string Fixed name
+    */
+    function enigtombFixName($strName)
+    {
+        $strName = trim($strName);
+        if (isset($GLOBALS['enigtombConfig']['namefixes'][$strName])) {
+            return $GLOBALS['enigtombConfig']['namefixes'][$strName];
+        } else {
+            return $strName;
+        }
+    }//function enigtombFixName(..)
+}
+
+?>
\ No newline at end of file