Merge pull request #2059 from mcrosson/android-feature-external_player
authordavilla <davilla@xbmc.org>
Fri, 25 Jan 2013 00:31:33 +0000 (16:31 -0800)
committerdavilla <davilla@xbmc.org>
Fri, 25 Jan 2013 00:31:33 +0000 (16:31 -0800)
[droid] External Player Support

180 files changed:
addons/metadata.album.universal/addon.xml
addons/metadata.album.universal/changelog.txt
addons/metadata.album.universal/resources/language/Afrikaans/strings.xml [new file with mode: 0644]
addons/metadata.album.universal/resources/language/Danish/strings.xml
addons/metadata.album.universal/resources/language/Finnish/strings.xml [new file with mode: 0644]
addons/metadata.album.universal/resources/language/Galician/strings.xml
addons/metadata.album.universal/resources/language/Japanese/strings.xml [new file with mode: 0644]
addons/metadata.album.universal/resources/language/Romanian/strings.xml [new file with mode: 0644]
addons/metadata.album.universal/resources/settings.xml
addons/metadata.common.fanart.tv/addon.xml
addons/metadata.common.fanart.tv/fanarttv.xml
addons/metadata.common.themoviedb.org/addon.xml
addons/metadata.common.themoviedb.org/tmdb.xml
addons/metadata.musicvideos.last.fm/addon.xml
addons/metadata.musicvideos.last.fm/resources/language/Danish/strings.xml
addons/metadata.musicvideos.last.fm/resources/language/Galician/strings.xml [new file with mode: 0644]
addons/metadata.musicvideos.last.fm/resources/language/Japanese/strings.xml [new file with mode: 0644]
addons/metadata.musicvideos.last.fm/resources/language/Portuguese (Brazil)/strings.xml
addons/metadata.themoviedb.org/addon.xml
addons/metadata.themoviedb.org/changelog.txt
addons/metadata.themoviedb.org/resources/language/Chinese (Traditional)/strings.xml [new file with mode: 0644]
addons/metadata.themoviedb.org/resources/language/Danish/strings.xml
addons/metadata.themoviedb.org/resources/language/Galician/strings.xml [new file with mode: 0644]
addons/metadata.themoviedb.org/resources/language/Japanese/strings.xml
addons/metadata.themoviedb.org/resources/language/Portuguese (Brazil)/strings.xml
addons/metadata.tvdb.com/addon.xml
addons/metadata.tvdb.com/changelog.txt
addons/metadata.tvdb.com/resources/language/Amharic/strings.xml [new file with mode: 0644]
addons/metadata.tvdb.com/resources/language/Chinese (Traditional)/strings.xml
addons/metadata.tvdb.com/resources/language/Danish/strings.xml
addons/metadata.tvdb.com/resources/language/Galician/strings.xml
addons/metadata.tvdb.com/resources/language/Japanese/strings.xml
addons/metadata.tvdb.com/resources/language/Malayalam/strings.xml [new file with mode: 0644]
addons/metadata.tvdb.com/resources/language/Portuguese (Brazil)/strings.xml
addons/metadata.tvdb.com/resources/language/Portuguese/strings.xml
addons/metadata.tvdb.com/tvdb.xml
addons/skin.confluence/720p/DialogExtendedProgressBar.xml
addons/skin.confluence/720p/DialogPVRGroupManager.xml
addons/skin.confluence/720p/DialogPVRGuideInfo.xml
addons/skin.confluence/media/DefaultAddonPVRClient.png [new file with mode: 0644]
addons/skin.touched
addons/weather.wunderground/addon.xml
addons/weather.wunderground/changelog.txt
addons/weather.wunderground/resources/language/Afrikaans/strings.po
addons/weather.wunderground/resources/language/Amharic/strings.po [new file with mode: 0644]
addons/weather.wunderground/resources/language/Arabic/strings.po
addons/weather.wunderground/resources/language/Basque/strings.po
addons/weather.wunderground/resources/language/Belarusian/strings.po
addons/weather.wunderground/resources/language/Bosnian/strings.po
addons/weather.wunderground/resources/language/Bulgarian/strings.po
addons/weather.wunderground/resources/language/Catalan/strings.po
addons/weather.wunderground/resources/language/Chinese (Simple)/strings.po
addons/weather.wunderground/resources/language/Chinese (Traditional)/strings.po
addons/weather.wunderground/resources/language/Croatian/strings.po
addons/weather.wunderground/resources/language/Czech/strings.po
addons/weather.wunderground/resources/language/Danish/strings.po
addons/weather.wunderground/resources/language/Dutch/strings.po
addons/weather.wunderground/resources/language/English/strings.po
addons/weather.wunderground/resources/language/Estonian/strings.po
addons/weather.wunderground/resources/language/Finnish/strings.po
addons/weather.wunderground/resources/language/French/strings.po
addons/weather.wunderground/resources/language/Galician/strings.po
addons/weather.wunderground/resources/language/German/strings.po
addons/weather.wunderground/resources/language/Greek/strings.po
addons/weather.wunderground/resources/language/Hebrew/strings.po
addons/weather.wunderground/resources/language/Hungarian/strings.po
addons/weather.wunderground/resources/language/Icelandic/strings.po
addons/weather.wunderground/resources/language/Italian/strings.po
addons/weather.wunderground/resources/language/Japanese/strings.po
addons/weather.wunderground/resources/language/Korean/strings.po
addons/weather.wunderground/resources/language/Lithuanian/strings.po
addons/weather.wunderground/resources/language/Macedonian/strings.po
addons/weather.wunderground/resources/language/Norwegian/strings.po
addons/weather.wunderground/resources/language/Persian/strings.po [new file with mode: 0644]
addons/weather.wunderground/resources/language/Polish/strings.po
addons/weather.wunderground/resources/language/Portuguese (Brazil)/strings.po
addons/weather.wunderground/resources/language/Portuguese/strings.po
addons/weather.wunderground/resources/language/Romanian/strings.po
addons/weather.wunderground/resources/language/Russian/strings.po
addons/weather.wunderground/resources/language/Serbian (Cyrillic)/strings.po
addons/weather.wunderground/resources/language/Serbian/strings.po
addons/weather.wunderground/resources/language/Slovak/strings.po
addons/weather.wunderground/resources/language/Slovenian/strings.po
addons/weather.wunderground/resources/language/Spanish (Argentina)/strings.po
addons/weather.wunderground/resources/language/Spanish (Mexico)/strings.po
addons/weather.wunderground/resources/language/Spanish/strings.po
addons/weather.wunderground/resources/language/Swedish/strings.po
addons/weather.wunderground/resources/language/Thai/strings.po
addons/weather.wunderground/resources/language/Turkish/strings.po
addons/weather.wunderground/resources/language/Ukrainian/strings.po
lib/enca/configure.ac
lib/libUPnP/Platinum/Source/Devices/MediaServer/PltMediaItem.cpp
lib/libdvd/libdvdcss/configure.ac
lib/libmad/configure.ac
lib/libmicrohttpd/configure.ac
project/Win32BuildSetup/buildpvraddons.bat
system/keymaps/keyboard.xml
system/keymaps/nyxboard/keyboard.xml
system/peripherals.xml
system/playercorefactory.xml
tools/android/depends/librtmp/Makefile
tools/android/depends/librtmp/librtmp-60-second-fix.patch [new file with mode: 0644]
tools/android/depends/xbmc-pvr-addons/Makefile
tools/darwin/depends/xbmc-pvr-addons/Makefile
xbmc/Application.cpp
xbmc/Application.h
xbmc/ApplicationMessenger.cpp
xbmc/ApplicationMessenger.h
xbmc/FileItem.cpp
xbmc/addons/Addon.cpp
xbmc/addons/AddonDatabase.cpp
xbmc/addons/AddonInstaller.cpp
xbmc/addons/GUIDialogAddonInfo.cpp
xbmc/android/activity/XBMCApp.cpp
xbmc/android/activity/XBMCApp.h
xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.cpp
xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAE.h
xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioAEHALOSX.cpp
xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioDevice.cpp
xbmc/cores/AudioEngine/Engines/CoreAudio/CoreAudioHardware.cpp
xbmc/cores/AudioEngine/Engines/SoftAE/SoftAE.cpp
xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h
xbmc/cores/AudioEngine/Utils/AEUtil.h
xbmc/cores/IPlayer.h
xbmc/cores/VideoRenderers/BaseRenderer.cpp
xbmc/cores/amlplayer/AMLPlayer.cpp
xbmc/cores/amlplayer/AMLPlayer.h
xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecVDA.cpp
xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
xbmc/cores/dvdplayer/Edl.cpp
xbmc/cores/omxplayer/OMXAudio.cpp
xbmc/cores/omxplayer/OMXPlayer.cpp
xbmc/cores/omxplayer/OMXPlayer.h
xbmc/cores/omxplayer/OMXPlayerVideo.cpp
xbmc/cores/omxplayer/OMXVideo.cpp
xbmc/cores/omxplayer/OMXVideo.h
xbmc/dialogs/GUIDialogContextMenu.cpp
xbmc/epg/GUIEPGGridContainer.cpp
xbmc/filesystem/test/TestRarFile.cpp
xbmc/filesystem/test/refRARnormal.rar [new file with mode: 0644]
xbmc/filesystem/test/refRARstored.rar [new file with mode: 0644]
xbmc/guilib/JpegIO.cpp
xbmc/input/XBMC_keysym.h
xbmc/input/XBMC_keytable.cpp
xbmc/input/XBMC_vkeys.h
xbmc/interfaces/json-rpc/PlayerOperations.cpp
xbmc/interfaces/legacy/File.cpp
xbmc/linux/DllBCM.h
xbmc/main/osx/SDLMain.mm
xbmc/music/tags/TagLoaderTagLib.cpp
xbmc/network/DNSNameCache.cpp
xbmc/network/osx/ZeroconfBrowserOSX.cpp
xbmc/network/upnp/UPnPInternal.cpp
xbmc/network/upnp/UPnPServer.cpp
xbmc/osx/CocoaInterface.h
xbmc/osx/CocoaInterface.mm
xbmc/osx/DarwinUtils.h
xbmc/osx/DarwinUtils.mm
xbmc/osx/HotKeyController.m
xbmc/peripherals/bus/osx/PeripheralBusUSB.cpp
xbmc/peripherals/devices/PeripheralCecAdapter.cpp
xbmc/pictures/Picture.cpp
xbmc/pictures/Picture.h
xbmc/powermanagement/PowerManager.cpp
xbmc/pvr/PVRDatabase.cpp
xbmc/pvr/windows/GUIWindowPVRChannels.cpp
xbmc/utils/EdenVideoArtUpdater.cpp
xbmc/utils/EdenVideoArtUpdater.h
xbmc/video/VideoDatabase.cpp
xbmc/video/VideoInfoScanner.cpp
xbmc/visualizations/Goom/goom2k4-0/gtk-gui-devel/configure.in
xbmc/win32/WIN32Util.cpp
xbmc/windowing/WinEventsSDL.cpp
xbmc/windowing/egl/EGLNativeTypeRaspberryPI.cpp
xbmc/windowing/egl/EGLNativeTypeRaspberryPI.h
xbmc/windowing/egl/WinSystemEGL.cpp
xbmc/windowing/osx/WinSystemOSX.mm
xbmc/windowing/windows/WinEventsWin32.cpp

index 710541c..b4b3f17 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="metadata.album.universal"
        name="Universal Album Scraper"
-       version="1.6.2"
+       version="1.6.4"
        provider-name="Olympia, Team XBMC">
   <requires>
     <import addon="xbmc.metadata" version="1.0"/>
              language="en"
              library="albumuniversal.xml"/>
   <extension point="xbmc.addon.metadata">
+    <summary lang="af">Universele Skraper vir Albums</summary>
     <summary lang="be">Universal Scraper for Albums</summary>
     <summary lang="bg">Универсален източник за информация за албуми</summary>
     <summary lang="ca">Arreplegador universal per àlbums</summary>
     <summary lang="cs">Univerzální zdroj získávání dat pro alba</summary>
-    <summary lang="da">Universel scraper til albummer</summary>
+    <summary lang="da">Universel Scraper til Albummer</summary>
     <summary lang="de">Universeller Scraper für Alben</summary>
     <summary lang="el">Universal Scraper Άλμπουμ</summary>
     <summary lang="en">Universal Scraper for Albums</summary>
     <summary lang="es">Scraper universal para álbumes</summary>
     <summary lang="es_AR">Scraper Universal para Albumes</summary>
     <summary lang="es_MX">Scraper Universal para Álbumes</summary>
+    <summary lang="fi">Universaali tiedonhakija albumitiedoille</summary>
     <summary lang="fr">Scraper universel pour les Albums</summary>
     <summary lang="gl">Scraper universal para os álbumes</summary>
     <summary lang="he">סקרייפר אוניברסלי עבור אלבומים</summary>
     <summary lang="hu">Univerzális album leolvasó</summary>
     <summary lang="it">Ricercatore Universale per Albums</summary>
+    <summary lang="ja">汎用アルバムスクレーパー</summary>
     <summary lang="ko">Universal Scraper for Albums</summary>
     <summary lang="lt">Universalus Albumų Scrapers</summary>
     <summary lang="mk">Универзален превземач за Албуми</summary>
     <summary lang="pl">Universal Scraper dla albumów muzycznych</summary>
     <summary lang="pt">Scraper universal para álbuns</summary>
     <summary lang="pt_BR">Scraper Universal para Álbuns</summary>
+    <summary lang="ro"> Scraper universal pentru albume</summary>
     <summary lang="ru">Универсальный скрапер для альбомов</summary>
     <summary lang="sk">Univerzálny zdroj získavania dát pre albumy</summary>
     <summary lang="sl">Univerzalni ponudnik informacij o albumih</summary>
     <summary lang="sv">Universiell albumskrapa</summary>
     <summary lang="zh">通用音乐专辑信息刮削器</summary>
+    <description lang="af">Hierdie skraper kollekteer inligting vanaf die volgende ondersteunde werwe: MusicBrainz, last.fm, allmusic.com en amazon.de, terwyl kunswerke gegryp word vanaf: fanart.tv, last.fm en allmusic.com. Dit kan veld vir veld gestel word vanaf watter werf jy die spesifieke inligting wil hê.&#10;&#10;Die aanvaklike soektog word altyd op MusicBrainz gedoen. Ingeval skakels van allmusic en/of amazon.de nie bygevoeg is op die MusicBrainz werf, kan velde vanaf allmusic.com en/of amazon.de nie gaan haal word nie (egter baie maklik om daardie ontbrekende skakels by te voeg).</description>
     <description lang="be">This scraper collects information from the following supported sites: MusicBrainz, last.fm, allmusic.com and amazon.de, while grabs artwork from: fanart.tv, last.fm and allmusic.com. It can be set field by field that from which site you want that specific information.&#10;&#10;The initial search is always done on MusicBrainz. In case allmusic and/or amazon.de links are not added on the MusicBrainz site, fields from allmusic.com and/or amazon.de cannot be fetched (very easy to add those missing links though).</description>
     <description lang="bg">Сваля информация от следните сайтове: MusicBrainz, last.fm, allmusic.com и amazon.de. Изобрания извлича от: fanart.tv, last.fm и allmusic.com. Можете да определите от кой сайт каква информация да се сваля.&#10;&#10;Първоначалното търсене винаги се прави в MusicBrainz. Когато не са налични препратки към allmusic и/или amazon.de на сайта на MusicBrainz извличането на информация от allmusic.com и/или amazon.de става невъзможно (добавянето им е много лесно).</description>
     <description lang="ca">Aquest arreplegador recull informació dels següents llocs: MusicBrainz, last.fm, allmusic.com i amazon.de, alhora que agafa art de: fanart.tv, last.fm i allmusic.com. Es pot configurar camp a camp des de quin lloc voleu aquella informació específica. &#10;&#10;La cerca inicial es realitza sempre a MusicBrainz. En el cas que els enllaços d'allmusic i amazon.de no s'afegeixin al lloc MusicBrainz els camps d'allmusic i amazon.de i/o amazon.com no es poden obtenir (tot i que són fàcils d'afegir aquests enllaços restants).</description>
     <description lang="cs">Tento zdroj získává data z následujících podporovaných stránek: MusicBrainz, last.fm, allmusic.com and amazon.de, while grabs artwork from: fanart.tv, last.fm a allmusic.com. Je možno podrobně nastavit, které informace si přejete získat ze kterých stránek.&#10;&#10;Prvotní vyhledání je vžy provedeno na MusicBrainz. V případě, že odkaz pro allmusic a nebo amazon.de není přidaný na stránku MusicBrainz, není možné údaje z allmusic.com a amazon.de stáhnout (avšak je velmi snadné tyto chybějící odkazy přidat).</description>
-    <description lang="da">Denne scraper samler information fra følgende sider: MusicBrainz, last.fm, allmusic.com og amazon.de, og henter illustrationer fra: fanart.tv, last.fm og allmusic.com. Den kan sættes felt for felt fra de sider du nu måtte ønske at få de pågældende informationer fra.&#10;&#10;Den første søgning er altid på MusicBrainz. Hvis ikke allmusic og/eller amazon.de linkene er tilføjet på MusicBrainz side, kan felter fra allmusic.com og/eller amazon.de ikke hentes (men det er helt enkelt at tilføje de manglende links).</description>
+    <description lang="da">Denne scraper samler information fra følgende sider: MusicBrainz, last.fm, allmusic.com og amazon.de og henter illustrationer fra: fanart.tv, last.fm og allmusic.com. Det kan vælges felt for felt, hvilke sider du ønsker, at få den pågældende information fra.&#10;&#10;Den første søgning er altid på MusicBrainz. Hvis ikke allmusic og/eller amazon.de linkene er tilføjet på MusicBrainz side, kan felter fra allmusic.com og/eller amazon.de ikke hentes (men det er helt enkelt at tilføje de manglende links).</description>
     <description lang="de">Dieser Scaper sammelt Informationen von den folgenden unterstützten Seiten: MusicBrainz, Last.fm, Allmusic.com und Amazon.de und lädt dabei Grafiken von: Fanart.tv, Last.fm und Allmusic.com. Es kann eingestellt werden, von welcher Seite die spezifischen Informationen stammen.⏎ ⏎ Die initialie Suche wird immer über MusicBrainz durchgeführt. Sollten keine Allmusic- und/oder Amazon.de-Links auf der MusicBrainz-Seite gesetzt worden sein, können Felder von Allmusic.com und/oder Amazon.de nicht geladen werden (fehlende Links können jedoch sehr einfach hinzugefügt werden).</description>
     <description lang="el">Αυτό το scraper συλλέγει πληροφορίες από τις ακόλουθες υποστηριζόμενες ιστοσελίδες: MusicBrainz, last.fm, allmusic.com και amazon.de, ενώ λαμβάνει εικόνες από τα: fanart.tv, last.fm και allmusic.com. Μπορεί να οριστεί πεδίο ανά πεδίο αναλόγως των πληροφοριών που επιθυμείτε από την εκάστοτε ιστοσελίδα.&#10;&#10;Η αρχική αναζήτηση γίνεται πάντα στο MusicBrainz. Σε περίπτωση που δεν έχουν προστεθεί σύνδεσμοι για το allmusic ή/και το amazon.de στο MusicBrainz, δε θα μπορούν να ληφθούν πεδία από το allmusic.com ή/και το amazon.de (ωστόσο είναι πολύ εύκολο να προστεθούν αυτοί οι ελλιπείς σύνδεσμοι).</description>
     <description lang="en">This scraper collects information from the following supported sites: MusicBrainz, last.fm, allmusic.com and amazon.de, while grabs artwork from: fanart.tv, last.fm and allmusic.com. It can be set field by field that from which site you want that specific information.&#10;&#10;The initial search is always done on MusicBrainz. In case allmusic and/or amazon.de links are not added on the MusicBrainz site, fields from allmusic.com and/or amazon.de cannot be fetched (very easy to add those missing links though).</description>
     <description lang="es">Este scraper recolecta información de las siguientes webs: MusicBrainz, last.fm, allmusic.com y amazon.de, mientras obtiene las imágenes de fanart.tv, last.fm y allmusic.com. Permite especificar de qué web se quiere obtener la información para cada campo. ⏎ ⏎ La búsqueda inicial se realiza siempre en MusicBrainz. En caso de que los enlaces a allmusic y/o amazon.de no estén añadidos en la web de MusicBrainz, no se podrán obtener los campos de información de estos dos sitios (aunque es muy fácil añadir esos enlaces que faltan).</description>
     <description lang="es_AR">Este scraper recolecta información de las siguientes webs: MusicBrainz, last.fm, allmusic.com y amazon.de, mientras obtiene las imágenes de fanart.tv, last.fm y allmusic.com. Permite especificar de qué web se quiere obtener la información para cada campo. ⏎ ⏎ La búsqueda inicial se realiza siempre en MusicBrainz. En caso de que los enlaces a allmusic y/o amazon.de no estén añadidos en la web de MusicBrainz, no se podrán obtener los campos de información de estos dos sitios (aunque es muy fácil añadir esos enlaces que faltan).</description>
     <description lang="es_MX">Este scraper obtiene información de los siguientes sitios soportados: MusicBrainz, last.fm, allmusic.com y amazon.de, mientras obtiene artwork de: fanart.tv, last.fm y allmusic.com. Puede ser configurado campo por campo de que sitio quieres esa información específica.⏎ ⏎ La búsqueda inicial siempre es hecha en MusicBrainz. En el caso en que los enlaces de allmusic y/o amazon.de no son agregados en el sitio de  MusicBrainz, campos de allmusic.com y/o amazon.de no pueden ser obtenidos. (De cualquier manera es muy fácil agregar esos enlaces no encontrados).</description>
+    <description lang="fi">Tämä tiedonhakija käyttää tietolähteinään seuraavia sivustoja:MusicBrainz, last.fm, allmusic.com ja amazon.de. Kuvat ja grafiikat haetaan seuraavilta sivustoilta: fanart.tv, last.fm ja allmusic.com. Voit määrittää tarkasti, miltä sivustoilta mitäkin tietoa haetaan.&#10;&#10;Ensimmäinen haku tehdään aina lähteestä MusicBrainz. Mikäli allmusic- ja/tai amazon.de-linkkejä ei ole lisätty MusicBrainz-sivustolle, haku ei onnistu (mutta puuttuvien linkkien lisääminen on helppoa).</description>
     <description lang="fr">Ce collecteur utilise les informations des sites suivant : MusicBrainz, last.fm, allmusic.com et amazon.de et télécharge les artwork depuis : fanart.tv, last.fm et allmusic.com. Vous pouvez spécifier quel site utiliser pour chaque information à employer.&#10;&#10;Le premier résultat est toujours en provenance de MusicBrainz. Dans le cas où allmusic et/ou amazon.de ne sont pas ajoutés sur le site MusicBrainz, les champs de allmusic.com et/ou amazon.de ne peuvent pas être traités (il est toutefois très simple d'ajouter ces liens manquants).</description>
     <description lang="gl">Este scraper obtén a información dende os seguintes sitios soportados: MusicBrainz, last.fm, allmusic.com e amazon.de, mentres que as ilustracións as obtén dende: fanart.tv, last.fm e allmusic.com. Pódese definir dende que sitios se obterá a información específica de cada campo.&#10;&#10;A busca inicial faise sempre en MusicBrainz. No caso de non estar engadidas as ligazón de allmusic e/ou amazon.de no sitio de MusicBrainz, os campos de allmusic.com e/ou amazon.de non se poderán obter (aínda que é moi doado de engadir as ligazóns).</description>
-    <description lang="he">This scraper collects information from the following supported sites: MusicBrainz, last.fm, allmusic.com and amazon.de, while grabs artwork from: fanart.tv, last.fm and allmusic.com. It can be set field by field that from which site you want that specific information.&#10;&#10;The initial search is always done on MusicBrainz. In case allmusic and/or amazon.de links are not added on the MusicBrainz site, fields from allmusic.com and/or amazon.de cannot be fetched (very easy to add those missing links though).</description>
+    <description lang="he">סקרייפר זה אוסף מידע מהתארים הנתמכים הבאים: TheAudioDb.com, MusicBrainz, last.fm, and allmusic.com, בעוד שמשיכת גרפיקה נעשית דרך: fanart.tv, htbackdrops.com, last.fm and allmusic.com. יש אפשרות להגדיר את זאת שדה אחר שדה מאיזה אתר ברצונך למשוך את המידע הספציפי.⏎ ⏎ החיפוש הפנימי נעשה תמיד דרך MusicBrainz. במקרה שקישור allmusic לא התווסף באתר MusicBrainz, השדות מאת allmusic.com אינן יכולות להימשך (למרות שזה מאוד קל להוסיף את הקישורים הריקים).</description>
     <description lang="hu">A leolvasó szöveges infókat gyűjt a támogatott oldalakról: MusicBrainz, last.fm, allmusic.com and amazon.de, valamint képeket a : fanart.tv, last.fm and allmusic.com-ról. Beállítható hogy honnan, és milyen infót gyűjtsön. A keresés mindig a MusicBrainz-on folyik, de könnyen hozzáadhatjuk a hiányzó találatokat az allmusic.com, vagy az amazon.de-ről.</description>
     <description lang="it">Questo ricercatore prende informazioni dai seguenti siti: MusicBrainz, last.fm, allmusic.com e amazon.de, mentre prende grafica da: fanart.tv, last.fm e allmusic.com. Puoi impostare campo per campo cosa prendere e da dove.⏎ ⏎La ricerca iniziale viene sempre fatta su MusicBrainz. In caso i links a allmusic e/o amazon.de non sono stati messi su MusicBrainz, i campi da questi siti non possono essere recuperati (è molto facile aggiungere quei link, comunque).</description>
+    <description lang="ja">このスクレーパーは、以下のサイトから情報を取得します:MusicBrainz, last.fm, allmusic.com, amazon.de。アートワークは以下のサイトから取得します:fanart.tv, last.fm, allmusic.com。各フィールドごとに、どの情報をどのサイトから取得するか、個別に指定することもできます。&#10;&#10;最初は MusicBrainz を使って検索が行われます。MusicBrainz サイトに allmusic や amazon.de のリンクが追加されていない場合は、allmusic.com / amazon.de からのフィールドは取得できません (リンクを追加するのは簡単ですが)。</description>
     <description lang="ko">This scraper collects information from the following supported sites: MusicBrainz, last.fm, allmusic.com and amazon.de, while grabs artwork from: fanart.tv, last.fm and allmusic.com. It can be set field by field that from which site you want that specific information.&#10;&#10;The initial search is always done on MusicBrainz. In case allmusic and/or amazon.de links are not added on the MusicBrainz site, fields from allmusic.com and/or amazon.de cannot be fetched (very easy to add those missing links though).</description>
     <description lang="lt">Šis Scrapers renka informaciją iš šių palaikomų svetainių: MusicBrainz, last.fm, allmusic.com ir/arba amazon.de, o scrap meno kūrinius iš: fanart.tv, last.fm ir/arba allmusic.com. Jis gali būti nustatytas srities lauke, iš kurio puslapio norite, kad konkrečią informaciją paimtu. Pradinė paieška visada atliekama remiantis MusicBrainz. Jei Allmusic ir/ar amazon.de nuorodos nėra įdėtos MusicBrainz svetainėje, laukuose allmusic.com ir/arba amazon.de negali būti realūs (nors įtraukti tas trūkstamas grandis labai lengva).</description>
     <description lang="mk">This scraper collects information from the following supported sites: MusicBrainz, last.fm, allmusic.com and amazon.de, while grabs artwork from: fanart.tv, last.fm and allmusic.com. It can be set field by field that from which site you want that specific information.⏎ ⏎ The initial search is always done on MusicBrainz. In case allmusic and/or amazon.de links are not added on the MusicBrainz site, fields from allmusic.com and/or amazon.de cannot be fetched (very easy to add those missing links though).</description>
@@ -69,6 +76,7 @@
     <description lang="pl">Ten scraper zbiera informacje z następujących stron: MusicBrainz, last.fm, allmusic.com oraz amazon.de, a grafiki pobiera z: fanart.tv, last.fm i allmusic.com. Dla każdego pola z informacjami można wybrać osobną stronę.&#10;&#10;Początkowe wyszukiwanie jest zawsze wykonywane na MusicBrainz. Gdy linki do allmusic i/lub amazon.de nie są dodane na stronie MusicBrainz, nie można pobrać tych pól z allmusic.com i/lub amazon.de (pomimo to brakujące linki można bardzo łatwo dodać).</description>
     <description lang="pt">Este scraper recolhe informações dos seguintes sítios:&#10;MusicBrainz, last.fm, allmusic.com e amazon.de, enquanto obtém artwork de: fanart.tv, last.fm e allmusic.com. Pode definir em cada campo o sítio do qual quer obter as informações. A pesquisa inicial é sempre feita no MusicBrainz. Caso os links allmusic e/ou amazon.de não tenham sido adicionados no sítio MusicBrainz, os campos do allmusic.com e/ou amazon.de não serão obtidos (no entanto, é muito fácil adicionar os links em falta). </description>
     <description lang="pt_BR">Este scraper coleta informações nos seguintes sites:&#10;MusicBrainz, last.fm, allmusic.com e amazon.de, enquanto pega os artwork de: fanart.tv, last.fm and allmusic.com. O Mesmo pode ser configurara campo a campo em que site você quer obter informações específicas. A procura inicial é sempre feita no MusicBrainz. No caso allmusic e/ou amazon.de não terem sido adicionados no site MusicBrainz, campos de allmusic.com e/ou amazona.de não serão buscados (embora seja muito fácil adicionar os links faltantes). </description>
+    <description lang="ro">Acest scraper colectează informații de la următoarele site-uri suportate: MusicBrainz, last.fm, allmusic.com și amazon.de. Elementele de grafica sunt accesate din: fanart.tv, last.fm și allmusic.com. Acesta poate fi setat câmp cu câmp de pe care site doriți informațiile specifice sa fie colectate ⏎ ⏎ Căutarea inițială se face întotdeauna pe MusicBrainz. În cazul în care Allmusic și / sau link-ul amazon.de nu sunt adăugate pe MusicBrainz, câmpurile din allmusic.com și / sau amazon.de nu pot fi colectate (este foarte ușor însă de adăugat aceste legături lipsă).</description>
     <description lang="ru">Данный скрапер собирает информацию со следующих сайтов:&#10;MusicBrainz, last.fm, allmusic.com и amazon.de, при этом получая обложки из: fanart.tv, last.fm и allmusic.com. Каждое поле можно настроить так, чтобы оно получало желаемую информацию с определенного вами сайта.⏎ ⏎ Начальный поиск всегда проводится на  MusicBrainz. Для allmusic и/или amazon.de ссылки на сайт MusicBrainz отсутствуют, поля с allmusic.com и/или amazon.de не могут быть заполнены (добавить пропущенные ссылки очень просто).</description>
     <description lang="sk">Tento zdroj získavania dát zbiera informácie z nasledujúcich podporovaných stránok: MusicBrainz, last.fm, allmusic.com a amazon.de, zatiaľ čo umeleckú grafiku získava zo stránok: fanart.tv, last.fm a allmusic.com. Je možné podrobne nastaviť, z ktorej stránky chcete získať konkrétne informácie.&#10;&#10;Počiatočné hľadanie je vždy na stránke MusicBrainz. V prípade, že odkaz pre allmusic a/alebo amazon.de nie je pridaný na stránku MusicBrainz, nie je možné získať údaje zo stránky allmusic.com a/alebo amazon.de (je ale veľmi jednoduché tieto chýbajúce odkazy pridať).</description>
     <description lang="sl">Ta ponudnik združuje informacije s sledečih strani: MusicBrainz, last.fm, allmusic.com in amazon.de; grafike prenaša z: fanart.tv, last.fm in allmusic.com. Vsako informacijo lahko prenesete z različne strani.&#10;&#10;Začeno iskanje je vedn na MusicBrainz. V primeru, da na MusicBrainz ni povezave do allmusic.com in/ali amazon.de, informacij od tam ne morete prenesti (a to težavo hitro lahko odpravite).</description>
index 4a72f7f..c3dc517 100644 (file)
@@ -1,3 +1,9 @@
+[B]1.6.4[/B]
+Disable last.fm options because last.fm suspended XBMC's API account
+
+[B]1.6.3[/B]
+Updated language files from Transifex
+
 [B]1.6.2[/B]
 Updated language files from Transifex
 
diff --git a/addons/metadata.album.universal/resources/language/Afrikaans/strings.xml b/addons/metadata.album.universal/resources/language/Afrikaans/strings.xml
new file mode 100644 (file)
index 0000000..6d06678
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Afrikaans language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/af/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30000">Gryp Album Duimnaelsketse vanaf fanart.tv</string>
+    <string id="30001">Gryp Album Duimnaelsketse vanaf Last.fm</string>
+    <string id="30002">Kry Album Resensie vanaf</string>
+    <string id="30003">Verkose Taal</string>
+    <string id="30004">Kry Album Gradering vanaf</string>
+    <string id="30005">Kry Album Style vanaf</string>
+    <string id="30006">Kry Album Stemmings vanaf</string>
+    <string id="30007">Kry Album Temas vanaf</string>
+    <string id="30008">Gryp Album Duimnaelsketse vanaf allmusic.com</string>
+    <string id="30009">Kunswerk</string>
+    <string id="30010">Gryp Album Duimnaelsketse vanaf theaudiodb.com</string>
+</strings>
index 51fb8bc..74917d0 100644 (file)
@@ -4,15 +4,15 @@
 <!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
 
 <strings>
-    <string id="30000">Hent album miniaturebilleder fra fanart.tv</string>
-    <string id="30001">Hent album miniaturebilleder fra Last.fm</string>
-    <string id="30002">Hent album anmeldelse fra</string>
-    <string id="30003">Foretrukken sprog</string>
-    <string id="30004">Hent album bedømmelse fra</string>
-    <string id="30005">Hent album stilarter fra</string>
-    <string id="30006">Hent album stemning fra</string>
-    <string id="30007">Hent album temaer fra</string>
-    <string id="30008">Hent album miniaturebilleder fra allmusic.com</string>
+    <string id="30000">Hent Miniaturebilleder til Albummer fra fanart.tv</string>
+    <string id="30001">Hent Miniaturebilleder til Albummer fra Last.fm</string>
+    <string id="30002">Hent Albumanmeldelse fra</string>
+    <string id="30003">Foretrukket Sprog</string>
+    <string id="30004">Hent Albumbedømmelse fra</string>
+    <string id="30005">Hent Albumstilarter fra</string>
+    <string id="30006">Hent Albumstemninger fra</string>
+    <string id="30007">Hent Albumtemaer fra</string>
+    <string id="30008">Hent Miniaturebilleder til Albummer fra allmusic.com</string>
     <string id="30009">Illustrationer</string>
-    <string id="30010">Hent album miniaturebilleder fra theaudiodb.com</string>
+    <string id="30010">Hent Miniaturebilleder til Albummer fra theaudiodb.com</string>
 </strings>
diff --git a/addons/metadata.album.universal/resources/language/Finnish/strings.xml b/addons/metadata.album.universal/resources/language/Finnish/strings.xml
new file mode 100644 (file)
index 0000000..38c749d
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Finnish language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/fi/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30000">Hae albumien pikkukuvat lähteestä fanart.tv</string>
+    <string id="30001">Hae albumien pikkukuvat lähteestä Last.fm</string>
+    <string id="30002">Hae albumien arvostelu lähteestä</string>
+    <string id="30003">Ensisijainen kieli</string>
+    <string id="30004">Hae albumien lähteestä</string>
+    <string id="30005">Hae albumien musiikkityylilajit lähteestä</string>
+    <string id="30006">Hae albumien mielentilat lähteestä</string>
+    <string id="30007">Hae albumien teemat lähteestä</string>
+    <string id="30008">Hae albumien pikkukuvat lähteestä allmusic.com</string>
+    <string id="30009">Kuvataide</string>
+    <string id="30010">Hae albumien pikkukuvat lähteestä theaudiodb.com</string>
+</strings>
index f1e10fc..c5c70c9 100644 (file)
@@ -10,6 +10,7 @@
     <string id="30003">Idioma preferido</string>
     <string id="30004">Obter a puntuación do álbum dende</string>
     <string id="30005">Obter os estilos do álbum dende</string>
+    <string id="30006">Obter Sonoridades do Álbum de</string>
     <string id="30007">Obter os temas do álbum dende</string>
     <string id="30008">Obter as miniaturas do álbum dende allmusic.com</string>
     <string id="30009">Ilustración</string>
diff --git a/addons/metadata.album.universal/resources/language/Japanese/strings.xml b/addons/metadata.album.universal/resources/language/Japanese/strings.xml
new file mode 100644 (file)
index 0000000..a4e5bf8
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Japanese language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/ja/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30000">fanart.tv からアルバムサムネールを取得</string>
+    <string id="30001">アルバムサムネールを Last.fm から取得</string>
+    <string id="30002">アルバムレビューの取得先</string>
+    <string id="30003">優先する言語</string>
+    <string id="30004">アルバム評価の取得先</string>
+    <string id="30005">アルバムスタイルの取得先</string>
+    <string id="30006">アルバムムードの取得先</string>
+    <string id="30007">アルバムテーマの取得先</string>
+    <string id="30008">アルバムサムネールを allmusic.com から取得</string>
+    <string id="30009">アートワーク</string>
+    <string id="30010">アルバムサムネールを theaudiodb.com から取得</string>
+</strings>
diff --git a/addons/metadata.album.universal/resources/language/Romanian/strings.xml b/addons/metadata.album.universal/resources/language/Romanian/strings.xml
new file mode 100644 (file)
index 0000000..6e4e3ea
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Romanian language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/ro/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30000">Colectează pictogramele albumelor din fanart.tv</string>
+    <string id="30001">Colectează pictogramele albumelor de la Last.fm</string>
+    <string id="30002">Descarcă review-urile albumelor de la </string>
+    <string id="30003">Limba preferată</string>
+    <string id="30004">Descarcă evaluarea albumelor de la </string>
+    <string id="30005">Descarcă stilul albumelor de la </string>
+    <string id="30006">Descarcă Album Moods de la </string>
+    <string id="30007">Descarcă tema albumelor de la </string>
+    <string id="30008">Colectează pictogramele albumelor de la allmusic.com</string>
+    <string id="30009">Pictograme</string>
+    <string id="30010">Colectează pictogramele albumelor de la theaudiodb.com</string>
+</strings>
index aee9648..b77bc72 100644 (file)
@@ -2,8 +2,7 @@
 <settings>
   <category label="128">
     <setting type="lsep" label="General Settings"/>
-      <setting label="30002" type="labelenum" values="last.fm|TheAudioDb.com|amazon.de|None" id="albumreviewsource" default="last.fm"/>
-      <setting label="30003" type="labelenum" values="en|de|es|fr|it|jp|pl|pt|ru|sv|tr|zh" id="lastfmlanguage" default="en" visible="eq(-1,0)"/>
+      <setting label="30002" type="labelenum" values="TheAudioDb.com|amazon.de|None" id="albumreviewsource" default="TheAudioDb.com"/>
       <setting type="sep"/>
       <setting label="30004" type="labelenum" values="MusicBrainz|allmusic.com|None" id="albumratingsource" default="MusicBrainz"/>
       <setting label="30005" type="labelenum" values="allmusic.com|None" id="albumstylessource" default="allmusic.com"/>
@@ -15,7 +14,6 @@
     <setting type="lsep" label="Artwork Settings"/>
       <setting label="30000" type="bool" id="fanarttvalbumthumbs" default="true"/>
       <setting label="30010" type="bool" id="tadbalbumthumbs" default="true"/>
-      <setting label="30001" type="bool" id="lastfmalbumthumbs" default="true"/>
       <setting label="30008" type="bool" id="allmusicalbumthumbs" default="true"/>
   </category>
 </settings>
index 381552e..ef673f1 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="metadata.common.fanart.tv"
        name="fanart.tv"
-       version="2.1.0"
+       version="2.1.1"
        provider-name="Team XBMC">
   <requires>
     <import addon="xbmc.metadata" version="1.0"/>
index 8ee1cd4..caff441 100644 (file)
@@ -13,7 +13,7 @@
                                <expression />
                        </RegExp>
                        <RegExp input="$$1" output="\1" dest="16">
-                               <expression noclean="1">&quot;artistthumb&quot;:\[(.*?)\],&quot;</expression>
+                               <expression noclean="1">&quot;artistthumb&quot;:\[(.*?)\}\]</expression>
                        </RegExp>
                        <RegExp input="$$16" output="&lt;thumb preview=&quot;\1/preview&quot;&gt;\1&lt;/thumb&gt;" dest="13">
                                <expression repeat="yes" noclean="1">&quot;id&quot;:&quot;\d*&quot;,&quot;url&quot;:&quot;([^&quot;]*)</expression>
index 8b8c9dd..1c00e60 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="metadata.common.themoviedb.org"
        name="The MovieDB common scraper functions"
-       version="2.9.0"
+       version="2.9.1"
        provider-name="Team XBMC">
   <requires>
     <import addon="xbmc.metadata" version="1.0"/>
index 12c28c5..ab8b3df 100644 (file)
                        <RegExp input="$$1" output="\1" dest="7">
                                <expression clear="yes" noclean="1">&quot;cast&quot;:\[([^\]]*)</expression>
                        </RegExp>
-                       <RegExp input="$$7" output="&lt;actor&gt;&lt;name&gt;\1&lt;/name&gt;&lt;role&gt;\2&lt;/role&gt;&lt;/actor&gt;" dest="2">
-                               <expression repeat="yes" fixchars="1">&quot;name&quot;:&quot;([^&quot;]*)&quot;,&quot;character&quot;:&quot;([^&quot;]*)</expression>
-                       </RegExp>
-                       <RegExp input="$$7" output="&lt;actor&gt;&lt;name&gt;\1&lt;/name&gt;&lt;role&gt;\2&lt;/role&gt;&lt;thumb&gt;http://cf2.imgobject.com/t/p/original\3&lt;/thumb&gt;&lt;/actor&gt;" dest="2+">
+                       <RegExp input="$$7" output="&lt;actor&gt;&lt;name&gt;\1&lt;/name&gt;&lt;role&gt;\2&lt;/role&gt;&lt;thumb&gt;http://cf2.imgobject.com/t/p/original\3&lt;/thumb&gt;&lt;/actor&gt;" dest="2">
                                <expression repeat="yes" fixchars="1">&quot;name&quot;:&quot;([^&quot;]*)&quot;,&quot;character&quot;:&quot;([^&quot;]*)&quot;,&quot;order&quot;:[0-9]*,&quot;cast_id&quot;:[0-9]*,&quot;profile_path&quot;:&quot;([^&quot;]*)&quot;</expression>
                        </RegExp>
+                       <RegExp input="$$7" output="&lt;actor&gt;&lt;name&gt;\1&lt;/name&gt;&lt;role&gt;\2&lt;/role&gt;&lt;/actor&gt;" dest="2+">
+                               <expression repeat="yes" fixchars="1">&quot;name&quot;:&quot;([^&quot;]*)&quot;,&quot;character&quot;:&quot;([^&quot;]*)&quot;,&quot;order&quot;:[0-9]*,&quot;cast_id&quot;:[0-9]*,&quot;profile_path&quot;:null</expression>
+                       </RegExp>
                        <expression noclean="1" />
                </RegExp>
        </ParseTMDBCast>
index 41d0bc8..d176db5 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="metadata.musicvideos.last.fm"
        name="Last.fm for Music Videos"
-       version="1.0.2"
+       version="1.0.3"
        provider-name="Team XBMC">
   <requires>
     <import addon="xbmc.metadata" version="1.0"/>
@@ -17,7 +17,7 @@
     <summary lang="bg">Сваля инф. за музикални клипове от Last.fm</summary>
     <summary lang="ca">Arreplegador de videos musicals de Last.fm</summary>
     <summary lang="cs">Zdroj dat hudebních videí Last.fm</summary>
-    <summary lang="da">Last.fm musikvideo scraper</summary>
+    <summary lang="da">Last.fm Scraper til Musikvideor </summary>
     <summary lang="de">Last.fm Musik Video Scraper</summary>
     <summary lang="el">Scraper Μουσικών Βίντεο από Last.fm</summary>
     <summary lang="en">Last.fm Music Video Scraper</summary>
     <summary lang="es_MX">Scraper de Vídeos de Música de Last.fm</summary>
     <summary lang="fi">Last.fm musiikkivideotietojen lataaja</summary>
     <summary lang="fr">Collecter les clips vidéos Last.fm</summary>
+    <summary lang="gl">Scraper de música e vídeo de Last.fm</summary>
     <summary lang="he">Last.fm וידאו קליפ מוזיקה</summary>
     <summary lang="hu">Last.fm videóklip leolvasó</summary>
     <summary lang="it">Scaper Video Musicale di Last.fm</summary>
+    <summary lang="ja">Last.fm 音楽ビデオスクレーパー</summary>
     <summary lang="ko">Last.fm 뮤직 비디오 자료수집기</summary>
     <summary lang="lt">Last.fm vaizdo klipų Scraper</summary>
     <summary lang="mk">Last.fm Превземач на Музички видеа</summary>
     <summary lang="nl">Last.fm Muziek Video Scraper</summary>
     <summary lang="pl">Scraper teledysków Last.fm</summary>
     <summary lang="pt">Scraper de música Last.fm</summary>
-    <summary lang="pt_BR">Scraper de Concertos Last.FM</summary>
+    <summary lang="pt_BR">Scraper de Videoclipes do Last.fm</summary>
     <summary lang="ro">Informaţii Videoclipuri de la Last.fm</summary>
     <summary lang="ru">Инфоресурс видеоклипов Last.fm</summary>
     <summary lang="se">Skrapa för Last.fm musik</summary>
     <summary lang="th">ข้อมูลวิดีโอเพลงจาก Last.fm</summary>
     <summary lang="zh">Last.fm音乐电视刮削器</summary>
     <description lang="af">Laai Musiek Video inligting af</description>
+    <description lang="am">የሙዚቃ ቪዲዮ መረጃ ማውረጃ </description>
     <description lang="be">Зладаваць зьвесткі пра музычнае відэа</description>
     <description lang="bg">Сваля информация за музикални клипове</description>
     <description lang="ca">Baixa informació de videos musicals</description>
     <description lang="cs">Stáhnout informace o hudebním videu</description>
-    <description lang="da">Hent musikvideo information</description>
+    <description lang="da">Hent information til Musikvideoer</description>
     <description lang="de">Musik Video Information herunterladen</description>
     <description lang="el">Λήψη πληροφοριών Μουσικών Βίντεο</description>
     <description lang="en">Download Music Video information</description>
     <description lang="es_MX">Descarga Información de Vídeos de Músca</description>
     <description lang="fi">Lataa musiikkivideoiden tiedot</description>
     <description lang="fr">Télécharger les informations des clips vidéos</description>
+    <description lang="gl">Descargar información da música e vídeo</description>
     <description lang="he">הורד מידע וידאו קליפ</description>
     <description lang="hu">Videóklip információk letöltése a Last.fm webhelyről</description>
     <description lang="it">Scarica informazioni Video Musicale</description>
+    <description lang="ja">音楽ビデオ情報をダウンロード</description>
     <description lang="ko">뮤직 비디오 정보 다운로드</description>
     <description lang="lt">Atsisiusti vaizdo klipų informaciją</description>
     <description lang="mk">Превземи информации за музичко видео</description>
     <description lang="nl">Muziek Video informatie downloaden</description>
     <description lang="pl">Pobieraj informacje o teledyskach z last.fm</description>
     <description lang="pt">Descarregar informação de vídeoclipe</description>
-    <description lang="pt_BR">Download de informação de Concertos</description>
+    <description lang="pt_BR">Baixar informações do videoclipe</description>
     <description lang="ro">Descărcare informaţii videoclipuri</description>
     <description lang="ru">Загружать сведения о видеоклипах</description>
     <description lang="se">Ladda ner musikvideoinformation från last.fm</description>
@@ -76,5 +81,6 @@
     <description lang="sv">Ladda ner musikvideoinformation</description>
     <description lang="th">ดาวน์โหลดข้อมูลวิดีโอเพลง</description>
     <description lang="zh">从Last.fm下载音乐电视信息</description>
+    <broken>broken due to last.fm suspended XBMC API Account</broken>
   </extension>
 </addon>
index e7a2276..5f974be 100644 (file)
@@ -4,6 +4,6 @@
 <!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
 
 <strings>
-    <string id="30000">Hent album miniaturebilleder fra fanart.tv</string>
-    <string id="30001">Hent album miniaturebilleder fra Last.fm</string>
+    <string id="30000">Hent Miniaturebilleder af Albummer fra fanart.tv</string>
+    <string id="30001">Hent Miniaturebilleder af Albummer fra Last.fm</string>
 </strings>
diff --git a/addons/metadata.musicvideos.last.fm/resources/language/Galician/strings.xml b/addons/metadata.musicvideos.last.fm/resources/language/Galician/strings.xml
new file mode 100644 (file)
index 0000000..e81599d
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Galician language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/gl/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30000">Obter as miniaturas do álbum dende fanart.tv</string>
+    <string id="30001">Obter as miniaturas do álbum dende Last.fm</string>
+</strings>
diff --git a/addons/metadata.musicvideos.last.fm/resources/language/Japanese/strings.xml b/addons/metadata.musicvideos.last.fm/resources/language/Japanese/strings.xml
new file mode 100644 (file)
index 0000000..449f5de
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Japanese language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/ja/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30000">fanart.tv からアルバムサムネールを取得</string>
+    <string id="30001">Last.fm からアルバムサムネールを取得</string>
+</strings>
index 8270dbb..1f146b2 100644 (file)
@@ -5,5 +5,5 @@
 
 <strings>
     <string id="30000">Obter miniaturas de álbuns de fanart.tv</string>
-    <string id="30001">Obter miniaturas de álbuns de Last.fm</string>
+    <string id="30001">Obter miniaturas de álbuns do Last.fm</string>
 </strings>
index c5b121b..15568ca 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="metadata.themoviedb.org"
        name="The Movie Database"
-       version="3.7.1"
+       version="3.7.2"
        provider-name="Team XBMC">
   <requires>
     <import addon="xbmc.metadata" version="1.0"/>
@@ -19,7 +19,7 @@
     <summary lang="bg">Сваля инф. за филми от TMDB</summary>
     <summary lang="ca">Arreplegador de pel·lícules TMDB</summary>
     <summary lang="cs">Zdroj zisku dat k filmům TMDB</summary>
-    <summary lang="da">TMDB film-scraper</summary>
+    <summary lang="da">TMDB Scraper til Film</summary>
     <summary lang="de">Film Scraper für TheMovieDB</summary>
     <summary lang="el">Scraper Ταινιών του TMDb</summary>
     <summary lang="en">TMDB Movie Scraper</summary>
@@ -29,6 +29,7 @@
     <summary lang="et">TMDB Filmi kaabits</summary>
     <summary lang="fi">TMDB elokuvatietojen lataaja</summary>
     <summary lang="fr">Collecteur de Films TMDB</summary>
+    <summary lang="gl">Scraper de Filme de TMDB</summary>
     <summary lang="he">TMDB סקרייפר סרטים</summary>
     <summary lang="hu">TMDB filmadat leolvasó</summary>
     <summary lang="it">Ricercatore per TMDB Movie</summary>
@@ -40,7 +41,7 @@
     <summary lang="nl">TMDb-filmscraper</summary>
     <summary lang="pl">Scraper filmowy TMDB</summary>
     <summary lang="pt">Scraper de filmes TMDb</summary>
-    <summary lang="pt_BR">Scraper Filmes TMDB</summary>
+    <summary lang="pt_BR">Scraper de Filmes TMDb</summary>
     <summary lang="ro">Scraper de filme TMDb</summary>
     <summary lang="ru">Инфоресурс на базе TMDB</summary>
     <summary lang="se">Skrapa för TMDb</summary>
@@ -54,7 +55,7 @@
     <description lang="bg">themoviedb.org е безплатна и отворена база от данни за филми. Задвижва се изцяло от потребителите си - хора като вас. Милиони хора по света всеки месец ползват TMDb, благодарение на многофункционалното API. Едновременно с това е и източник на информация за медийни центрове, като XBMC, които извличат метаинформация за филми, постери и Fanart.</description>
     <description lang="ca">themoviedb.org és una base de dades oberta i lliure. Es manté completament per usuaris, per gent com tu. TMDb s'utilitza actualment per milions de persones cada mes i amb la seva potent API també és utilitzada per molts centre media populas com l'XBMC per obtenir les meta-dades de pel·lícules, pósters i fanart per enriquir l'experiència d'usuari.</description>
     <description lang="cs">themoviedb.org je otevřená a svobodná databáze filmů. Je řízena uživateli, tedy lidmi jako vy. TMDB je každý měsíc používána milióny lidí a se svým výborným API je také používano mnoha populárními mediálními centry jako je XBMC, k získávání metadat, plagátů a fanartu filmů pro obohacení uživatelského zážitku.</description>
-    <description lang="da">themoviedb.org er en fri og åben filmdatabase. Den er helt og aldeles brugerdrevet af folk som dig. TMDb bruges i øjeblikket af millioner af mennesker hver måned, og med deres kraftfulde API er den også brugt af mange populære mediecentre som XBMC til at hente metadata, plakater og fankunst til film, for at berige brugerens oplevelse.</description>
+    <description lang="da">themoviedb.org er en fri og åben filmdatabase. Den er helt og aldeles brugerdrevet af folk som dig. TMDb bruges i dag af millioner af mennesker hver måned, og med deres kraftfulde API er den også brugt af mange populære mediecentre såsom XBMC til at hente metadata, plakater og fankunst til film, for at berige brugerens oplevelse.</description>
     <description lang="de">TheMovieDB.org ist eine freie und offene Filmdatenbank. Sie wird von Benutzern wie Dir betrieben. TheMovieDB wird zur Zeit monatlich von Millionen genutzt. Mit der mächtigen API wird sie auch von vielen Media-Center, wie z.B. XBMC, genutzt um Film Metadaten, Poster und Fanarts zu beziehen, damit der Benutzer sein Media-Center verschönern kann.</description>
     <description lang="el">Το themoviedb.org είναι μία δωρεάν και ανοικτή βάση δεδομένων ταινιών. Διαχειρίζεται πλήρως από ανθρώπους σαν και εσάς. Το TMDb χρησιμοποιείται από εκατομμύρια ανθρώπους κάθε μήνα, και με το πανίσχυρο API τους, χρησιμοποιείται και από πολλά δημοφιλή κέντρα πολυμέσων όπως το XBMC για να λαμβάνουν μετα-δεδομένα Ταινιών, Αφίσες και Fanart για εμπλουτισμό της εμπειρίας του χρήστη.</description>
     <description lang="en">themoviedb.org is a free and open movie database. It's completely user driven by people like you. TMDb is currently used by millions of people every month and with their powerful API, it is also used by many popular media centers like XBMC to retrieve Movie Metadata, Posters and Fanart to enrich the user's experience.</description>
     <description lang="et">themoviedb.org on tasuta ja vaba juurdepääsuga filmide andmebaas.See on täielikult koostatud sinu sarnaste inimeste poolt. TMDb on hetkel igapäevaselt kasutuses miljonite inimeste poolt ja seda kasutavad paljud populaarsed meediakeskused, nagu ka XBMC, filmiandmete, fännikunsti ja posterite leidmiseks.</description>
     <description lang="fi">themoviedb.org on vapaa ja avoin elokuvatietokanta. Se on täysin käyttäjien ylläpitämä. TMDb:a käyttää miljoonat ihmiset joka kuukausi, sitä käytetään käyttäjäkokemuksen rikastuttamiseen monissa muissakin suosituissa XBMC:n kaltaisissa ohjelmissa lataamalla elokuvien tietoja, julisteita ja fanitaidetta.</description>
     <description lang="fr">themoviedb.org est une base de données gratuite et libre d'accès de films. Elle est totalement gérée par les utilisateurs. TMDb est actuellement utilisée par des millions de personnes chaque mois et, grâce à ses puissantes fonctions et routines, elle est également utilisée par de nombreux Media Centers populaires comme XBMC aux fins de récupération des Métadonnées, Posters et Fanarts des films en vue d'améliorer l'expérience de l'utilisateur.</description>
+    <description lang="gl">themoviedb.org é una base de datos sobre filmes libre e aberta, impulsada por xente coma ti. Na actualidade TMDb é usada por millóns de persoas todos os meses, e grazas ó seu potente API, usado por algúns dos centros multimedia máis populares como XBMC para obter a información dos filmes, Posters e Fanart e así mellorar a experiencia do usuario.</description>
     <description lang="he">themoviedb.org is a free and open movie database. It's completely user driven by people like you. TMDb is currently used by millions of people every month and with their powerful API, it is also used by many popular media centers like XBMC to retrieve Movie Metadata, Posters and Fanart to enrich the user's experience.</description>
     <description lang="hu">A themoviedb.org egy ingyenes és nyílt filmadatbázis. Teljes egészében olyan felhasználók töltik fel, mint például Te. A TMDb-t havonta emberek milliói használják és a hatékony API-n keresztül számos népszerű média center is, mint például az XBMC a filmadatok, poszterek, fanartképek letöltésére.</description>
     <description lang="it">themoviedb.org è un database libero e aperto. E' gestito da utenti e persone come te. Viene usato correntemente da millioni di persone ogni mese e con le sue potenti API, è anche utilizzato da molti popolari media centers come XBMC per ottenere Metadati, Posters e Fanart di film per arricchire la loro esperienza utente.</description>
+    <description lang="ja">themoviedb.org は自由でオープンな映画データベースです。その運営は、完全にユーザ主導で行われています。TMDb は現在毎月何百万人もの人に利用されているほか、XBMC のようなメディアセンターも、TMDb のパワフルな API を介して映画メタデータ、ポスター、ファンアートを取得し、使い勝手を向上させるなどして使っています。</description>
     <description lang="ko">themoviedb.org 는 전적으로 여러분 같은 이용자가 주도하는 무료 공개 영화 데이터베이스입니다. TMDb 는 현재 매월 수백만의 사람들이 강력한 API와 함께 사용하고 있을 뿐만 아니라 XBMC 같은 많은 미디어 센터가 영화 정보, 포스터, 팬아트를 가져와 풍성한 사용자 경험을 제공하는데 이용되고 있습니다.</description>
     <description lang="lt">themoviedb.org yra nemokama ir atvira Filmų duomenų bazė. Jis remiamas ir skaitinamas tokių pat vartotojų kaip ir jūs. TMDb šiuo metu naudoja milijonai žmonių kiekvieną mėnesį, ir turi galingą API (informacinį centrą), taip pat naudojamas daug populiarių žiniasklaidos centrų. Pavyzdžiui XBMC gauti filmo(-ų) metaduomenis, plakatus ir FanArt siekiant praturtinti vartotojo patirtį.</description>
     <description lang="mk">themoviedb.org is a free and open movie database. It's completely user driven by people like you. TMDb is currently used by millions of people every month and with their powerful API, it is also used by many popular media centers like XBMC to retrieve Movie Metadata, Posters and Fanart to enrich the user's experience.</description>
     <description lang="nl">Themoviedb.org is een vrije en open filmdatabank. Gebruikers zoals jij vormen de motor van deze site. Op dit moment gebruiken elke maand miljoenen mensen TMDb. De krachtige API van de site laat mediacenters zoals XBMC toe om metadata, posters en fanart op te halen en zo de gebruikerservaring te verrijken.</description>
     <description lang="pl">themoviedb.org jest wolną i otwartą filmową bazą danych. Jest zarządzana całkowicie przez osoby takie jak Ty. Obecnie odwiedzają ją miliony osób miesięcznie, a dzięki dostępności API jest używana w wielu media center, takich jak XBMC, do pobierania informacji o filmach, plakatów i fanartów.</description>
     <description lang="pt">O themoviedb.org é uma base de dados de filmes livre e aberta. É actualizado inteiramente por pessoas como você e usado por milhões todos os meses. Com o poderoso motor de busca disponível, também se tornou no favorito para muitos programas de centro de média, como o popular XBMC, para obter informação, posters e fanart que enriquecem a experiência do utilizador.</description>
-    <description lang="pt_BR">themoviedb.org é um banco de dados aberto e gratuito. É completamente operacionalizado por pessoas como você. TMDb é usado atualmente por milhares de pessoas a cada mês e com sua poderosa API, é usado por muitos media centers como XBMC para buscar Metadados de Filtes, Posters e Fanart para enriquecer a experiência de uso.</description>
+    <description lang="pt_BR">O themoviedb.org é um banco de dados aberto e gratuito. É completamente operado por pessoas como você. O TMDb é atualmente usado por milhares de pessoas a cada mês e com sua poderosa API, é usado por muitas centrais de mídia populares como o XBMC para buscar metadados, cartazes e fanart de filmes para enriquecer a experiência do usuário.</description>
     <description lang="ro">themoviedb.org este o bază de date de filme gratuită și deschisă. Este actualizată de oameni ca și tine. TMDb este curent folosită de milioane de oameni în fiecare lună și datorită API-ului puternic, este de asemena folosită de multe centre media populare ca XBMC pentru a obține informații despre filme, afișe și imagini produse de fani (Fanart) care îmbogățesc experiența utilizatorului.</description>
     <description lang="ru">themoviedb.org — это бесплатная и открытая база данных фильмов. Она полностью поддерживается обычными людьми. В данный момент сайт TMDb используют миллионы людей каждый месяц, и благодаря мощному API его могут использовать различные популярные медиацентры, такие как XBMC, чтобы получать метаданные, постеры и фанарт для фильмов с целью красивого оформления интерфейса.</description>
     <description lang="se">themoviedb.org är en fri och öppen filmdatabas. Det drivs helt av människor som dig. TMDb används av miljontals människor varje månad och med deras kraftfulla API, är det också använt av många populära mediacenter som XBMC för att hämta filmmetadata, omslag och fanart för att förgylla användarens upplevelse.</description>
     <description lang="sk">themoviedb.org je voľná a otvorená databáza filmov. Je výlučne vedená užívateľmi ako si ty. TMDb je každý mesiac používaná miliónmi ľudí, a vďaka svojmu výkonnému API je veľmi populárna medzi 'media centrami' ako XBMC pre sťahovanie filmových metadát, plagátov a fanartov pre obohatenie  zážitkov.</description>
     <description lang="sl">themoviedb.org je brezplačna in prosta baza filmov. Je v celoti ustvarjena s strani uporabnikov kot ste vi. TMDB uporablja miljone ljudi in z njihovo močno API, jo lahko uporabljate tudi v multimedijskih centrih kot XBMC, s tem prenesete informacije o filmih, plakate in ozadja ter s tem popestrite uporabniško izkušnjo.</description>
-    <description lang="sv">themoviedb.org är en gratis och öppen filmdatabas. Den drivs helt av människor som du. TMDb används för närvarande av miljoner människor varje månad och med deras kraftfulla API används den även av många populära mediacenter som t.ex. XBMC för att hämta metadata om filmen, affischer och fanart för att berika användarens upplevelse.</description>
+    <description lang="sv">themoviedb.org är en gratis och öppen filmdatabas. Den drivs helt av människor som du. TMDb används för närvarande av miljoner människor varje månad och med deras kraftfulla API används den även av många populära mediacenter som t.ex. XBMC för att hämta metadata om filmer, affischer och fanart för att berika användarens upplevelse.</description>
     <description lang="th">themoviedb.org เป็นฐานข้อมูลภาพยนตร์ที่ฟรีและเปิดกว้าง. มันถูกขับเคลื่อนจากผู้ใช้ โดยคนเช่นคุณ. TMDb ปัจจุบันมีการใช้โดยคนนับล้านในแต่ละเดือน และมีประสิทธิภาพด้วย API ของพวกเขา , มันยังถูกใช้โดยหลายศูนย์สื่อที่นิยมเช่น XBMC เพื่อดึง อธิบายข้อมูลภาพยนตร์, โปสเตอร์และ แฟนอาร์ต เพื่อเพิ่มประสบการณ์ของผู้ใช้</description>
     <description lang="zh">themoviedb.org是一个开放和自由的电影数据库。它完全由象你一样的用户来掌控。TMDb每月为上百万用户提供服务,并通过强大的API界面为许多流行的媒体中心系统如XBMC提供电影资料、封面海报和同人画以丰富用户的体验。</description>
     <platform>all</platform>
index b5a25aa..c242425 100644 (file)
@@ -1,3 +1,6 @@
+[B]3.7.2[/B]
+- updated language files from Transifex
+
 [B]3.7.1[/B]
 - updated language files from Transifex
 
diff --git a/addons/metadata.themoviedb.org/resources/language/Chinese (Traditional)/strings.xml b/addons/metadata.themoviedb.org/resources/language/Chinese (Traditional)/strings.xml
new file mode 100644 (file)
index 0000000..84f20c2
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Chinese (Traditional) language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/zh_TW/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30000">啟用 Fanart</string>
+</strings>
index 8274222..ed87772 100644 (file)
@@ -4,11 +4,11 @@
 <!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
 
 <strings>
-    <string id="30000">Aktiver fankunst</string>
-    <string id="30001">Foretræk trailere fra HD-trailers.net</string>
-    <string id="30002">Foretrukket sprog</string>
+    <string id="30000">Aktivér Fankunst</string>
+    <string id="30001">Foretræk Trailer fra HD-trailers.net</string>
+    <string id="30002">Foretrukket Sprog</string>
     <string id="30003">Hent bedømmelse fra</string>
-    <string id="30004">Aktiver trailer (YouTube)</string>
+    <string id="30004">Aktivér Trailer (YouTube)</string>
     <string id="30005">Behold original titel</string>
-    <string id="30006">Foretrukket certificeringsland</string>
+    <string id="30006">Foretrukket land til aldersvejledninger</string>
 </strings>
diff --git a/addons/metadata.themoviedb.org/resources/language/Galician/strings.xml b/addons/metadata.themoviedb.org/resources/language/Galician/strings.xml
new file mode 100644 (file)
index 0000000..bc684ae
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Galician language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/gl/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30000">Habilitar Fanart</string>
+    <string id="30001">Preferir os Avances de HD-Trailers.net</string>
+    <string id="30002">Idioma Preferido</string>
+    <string id="30003">Obter a puntuación dende</string>
+    <string id="30004">Habilitar Avance (YouTube)</string>
+    <string id="30005">Manter o Título Orixinal</string>
+    <string id="30006">País de Certificación Preferido</string>
+</strings>
index 92e38c5..1863d1a 100644 (file)
@@ -6,8 +6,9 @@
 <strings>
     <string id="30000">ファンアートを有効にする</string>
     <string id="30001">HD-Trailers.net の予告編を優先</string>
-    <string id="30002">優先言語</string>
+    <string id="30002">優先する言語</string>
     <string id="30003">評価の取得先</string>
     <string id="30004">予告編を有効にする (YouTube)</string>
     <string id="30005">オリジナルタイトルを保持</string>
+    <string id="30006">優先する証明書の国</string>
 </strings>
index 1e936a7..fae53d2 100644 (file)
@@ -5,10 +5,10 @@
 
 <strings>
     <string id="30000">Ativar Fanart</string>
-    <string id="30001">Preferir Trailer de HD-Trailers.net</string>
-    <string id="30002">Linguagem Preferida</string>
-    <string id="30003">Pegar classificação de</string>
-    <string id="30004">Ativar Trailer (YouTube)</string>
-    <string id="30005">Manter o Título Original</string>
-    <string id="30006">Preferir Certificação do País</string>
+    <string id="30001">Preferir trailer de HD-Trailers.net</string>
+    <string id="30002">Idioma preferido</string>
+    <string id="30003">Obter classificação de</string>
+    <string id="30004">Ativar trailer (YouTube)</string>
+    <string id="30005">Manter o título original</string>
+    <string id="30006">Preferir país de certificação</string>
 </strings>
index 34e59ec..4e0bcb7 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="metadata.tvdb.com"
        name="The TVDB"
-       version="1.4.3"
+       version="1.4.5"
        provider-name="Team XBMC">
   <requires>
     <import addon="xbmc.metadata" version="1.0"/>
@@ -15,7 +15,7 @@
     <summary lang="be">Fetch TV show metadata from TheTVDB.com</summary>
     <summary lang="bg">Изтегля информация за ТВ Сериали от TheTVDB.com</summary>
     <summary lang="cs">Získat metadata televizního pořadu z TheTVDB.com</summary>
-    <summary lang="da">Hent TV serie metadata fra TheTVDB.com</summary>
+    <summary lang="da">Hent metadata til TV-serier fra TheTVDB.com</summary>
     <summary lang="de">Scraper für Fernsehserien von TheTVDB.com</summary>
     <summary lang="el">Λήψη μετα-δεδομένων σειρών από το TVDB.com</summary>
     <summary lang="en">Fetch TV show metadata from TheTVDB.com</summary>
     <summary lang="es_MX">Obtener metadata de Series desde TheTVDB.com</summary>
     <summary lang="fi">Nouda TV-ohjelmien tietoja TheTVDB.com -sivustolta</summary>
     <summary lang="fr">Collecter les métadonnées des séries TV depuis TheTVDB.com</summary>
+    <summary lang="gl">Obter os metadatos das series de TV dende TheTVDB.com</summary>
     <summary lang="he">הבא מידע </summary>
     <summary lang="hu">TV műsor információk a TheTVDB.com-ról</summary>
     <summary lang="is">Sækja Sjónvarpsþátta lýsigögn frá TheTVDB.com</summary>
     <summary lang="it">Scarica informazioni programma TV da TheTVDB.com</summary>
+    <summary lang="ja">TheTVDB.com からTV番組メタデータを取得</summary>
     <summary lang="ko">TheTVDB.com 에서 TV 쇼 정보 가져오기</summary>
     <summary lang="lt">Parsiųsti TV šou (serialo) metaduomenis iš TheTVDB.com</summary>
     <summary lang="mk">Превземи ТВ шоу метаподаток од TVDB.com</summary>
     <summary lang="nl">Seriemetadata ophalen van TheTVDB.com</summary>
     <summary lang="pl">Pobieraj dane o serialach TV z TheTVDB.com</summary>
     <summary lang="pt">Obtenha dados para séries TV de TVDB.com</summary>
-    <summary lang="pt_BR">Obtenha dados dos Seriados de TheTVDB.com</summary>
+    <summary lang="pt_BR">Obtenha dados dos seriados do TheTVDB.com</summary>
     <summary lang="ro">Obţineţi datele despre seriale de la TheTVDB.com</summary>
     <summary lang="ru">Загрузка информации о ТВ-шоу с TheTVDB.com</summary>
     <summary lang="se">Skrapa för TV-seriemetadata från TVDB.com</summary>
     <summary lang="th">เรียกข้อมูลหลักของรายการทีวี จาก TheTVDB.com</summary>
     <summary lang="uk">Отримання відомостей про серіали із TheTVDB.com</summary>
     <summary lang="zh">从TheTVDB.com获取电视剧集信息</summary>
+    <summary lang="zh_TW">從 TheTVDB.com 取得電視節目資訊</summary>
     <description lang="af">TheTVDB.com is 'n TV Skraper. Die werf is 'n massiewe oop databasis wat deur enige iemand verander kan word en bevat vol meta data vir baie vertonings in verskillende tale. Alle inhoud en prente op die werf is bygedra deur sy gebruikers vir sy gebruikers en het 'n hoë standaard van kwaliteit. Die databasis skema en webruimte is oopbron onder die GPL.</description>
     <description lang="be">TheTVDB.com is a TV Scraper. The site is a massive open database that can be modified by anybody and contains full meta data for many shows in different languages. All content and images on the site have been contributed by their users for users and have a high standard or quality. The database schema and website are open source under the GPL.</description>
     <description lang="bg">TheTVDB.com предоставя информация за ТВ Сериали. Сайтът представлява една огромна, безплатна и отворена база от данни. Всеки може да редактира (наличната информация) или да добавя нова. Съдържа подробни данни за много сериали и на различни езици. Цялото съдържание на сайта и публикуваните изображения са предоставени от потребители за потребителите. Съдържанието на сайтът и базата от данни е с отворен код под лиценза GPL.</description>
     <description lang="cs">TheTVDB.com je zdroj metadat pro televizní pořady. Stránka je masivní otevřená databáze, kterou může kdokoliv upravovat a obsahuje veškerá metadata v různých jazycích k mnoha pořadům. Všechen obsah a obrázky jsou poskytnuty uživateli pro uživatele a mají vysokou kvalitu. Návrhový vzor databáze a webová stránka jsou svobodný software licencovaný pod GPL.</description>
-    <description lang="da">TheTVDB.com er en TV-scraper. Siden er en massiv åben database, som alle kan modificere, og den indeholder fyldestgørende metadata for mange serier på forskellige sprog. Alt indhold og billeder på siden er tilføjet af brugere og har en høj standard eller kvalitet. Databasens model og hjemmeside er open source under GPL.</description>
+    <description lang="da">TheTVDB.com er en TV-scraper. Siden er en massiv åben database, som alle kan modificere, og den indeholder fyldestgørende metadata for mange serier på forskellige sprog. Alt indhold og alle billeder på siden er tilføjet af brugere og har en høj standard eller kvalitet. Databasens model og webside er open source under GPL.</description>
     <description lang="de">TheTVDB.com ist ein Scraper für TV-Serien. Die Seite hat eine riesige offene Datenbank, welche von jedem geändert werden kann. Sie enthält weitreichende Meta-Daten für viele TV-Serien in verschiedenen Sprachen. Alle Inhalte und Bilder dieser Seite stammen von User für User und haben eine hohe Qualität. Das Datenbankschema und die Webseite sind OpenSource unter dem GPL-Recht</description>
     <description lang="el">Το TVDB.com είναι ένα Scraper Τηλεόρασης. Η ιστοσελίδα είναι μία τεράστια ανοικτή βάση δεδομένων η οποία μπορεί να τροποποιηθεί από τον καθένα και περιέχει πλήρη μετα-δεδομένα για πολλές σειρές σε διάφορες γλώσσες. Όλο το περιεχόμενο και οι εικόνες στην ιστοσελίδα προέρχονται από τους χρήστες του, και έχουν υψηλά πρότυπα ποιότητας. Το σχήμα της βάσης δεδομένων και της ιστοσελίδας είναι ανοικτού κώδικα υπό το GPL.</description>
     <description lang="en">TheTVDB.com is a TV Scraper. The site is a massive open database that can be modified by anybody and contains full meta data for many shows in different languages. All content and images on the site have been contributed by their users for users and have a high standard or quality. The database schema and website are open source under the GPL.</description>
     <description lang="es_MX">TheTVDB.com es un scraper de series de TV. Este sitio es una base de datos masiva y abierta que puede ser modicada por cualquiera y contiene metadata completa para muchas series en diferentes lenguajes. Todo el contenido e imágenes del sitio han sido contribuidos por sus usuarios para usuarios, los cuales tienen un alto estándar de calidad. El esquema de la base de datos y el sitio web son de código abierto bajo GPL.</description>
     <description lang="fi">TheTVDB.com on TV-ohjelmatietojen lataaja. Sivusto on massiivinen avoin tietokanta, jonka tietoja kaikki pystyy muokkaamaan ja se sisältää täydet tiedot monista TV-ohjelmista usealla eri kielellä. Kaikki sivuston sisältö ja kuvat on korkealaatuisia ja käyttäjien lahjoittamia. Tietokanta ja sivusto ovat GPL-lisenssin alaisia.</description>
     <description lang="fr">TheTVDB.com est un collecteur TV. Le site est une immense base de données libre pouvant être modifiée par tout le monde et contenant toutes les informations de nombreuses séries en différentes langues. Les informations et images du site proviennent de contributions d'utilisateurs pour les usagers et sont d'excellentes qualités. Le schéma de la base de données et le site Web sont en open source sous licence GPL.</description>
+    <description lang="gl">TheTVDB.com é un Scraper de TV. O sitio é unha enorme base de datos aberta que calquera pode modificar e contén todos os metadatos de centos de series en diferentes idiomas. Todo o contido e imaxes do sitio foi proporcionado polos seus usuarios e amosa un grande estándar de calidade. O esquema da base de datos e o sitio web son de código aberto baixo licenza GPL.</description>
     <description lang="he">TheTVDB.com הוא סקרייפר עבור תוכניות טלוויזיה. אתר זה הוא מסד נתונים פתוח עצום אשר כל אחד יכול לערוך ומכיל פרטים מלאים להרבה תוכניות טלוויזיה בשפות שונות. כל התכנים והתמונות באתר נתרמו ע"י המשתמשים שלה עבור משתמשים וישנו סטנדרט גבוה או איכות גבוהה. תכנית המסד נתונית והאתר הם בקוד פתוח תחת ה-GPL.</description>
     <description lang="hu">A TheTVDB.com egy TV műsor leolvasó. Az oldal egy hatalmas adatbázis amit bárki szabadon módosíthat és rengeteg TV műsor adatait tartalmazza különböző nyelveken. A minőségi tartalom és képanyag ezen az oldalon felhasználóktól származik más felhasználók számára. Az adatbázisstruktúra és a weblap nyílt forráskódú a GPL licenc alatt.</description>
     <description lang="it">TheTVDB.com è uno scraper di programmi TV. Questo sito è un enorme database che può essere modificato da chiunque, e contiene tutte le informazioni di molti show in varie lingue. Tutti i contenuti e le immagini sul sito sono stati forniti dagli utenti, ed hanno un alto standard di qualità. Lo schema del database e il sito internet sono open source sotto licenza GPL.</description>
+    <description lang="ja">TheTVDB.com は TV スクレーパーです。本サイトは強力でオープンなデータベースで、誰もが追加修正可能です。テレビ番組のメタデータは多くの言語で提供されています。番組情報や画像データはユーザにより提供され、そのクオリティは非常に高いものです。データベーススキーマと web サイトは GPL ライセンスのオープンソースです。</description>
     <description lang="ko">TheTVDB.com 은 TV 자료수집기입니다. 위 사이트는 이용자가 직접 수정할 수 있으며 다양한 언어의 완전한 메타 데이터를 포함하는 방대한 오픈 데이터베이스입니다. 사이트의 모든 콘텐츠와 이미지들은 사이트 이용자들이 다른 이용자들을 위해 제공한 것이며 높은 품질을 가지고 있습니다. 웹사이트와 데이터베이스 스키마는 GLP 오픈소스입니다.</description>
     <description lang="lt">TheTVDB.com yra TV Scraper. Ši svetainė yra didžiulė atvira duomenų bazė, kuria gali pakeisti bet kas ir kurioje visa meta duomenis bazė daugelo šou įvairiomis kalbomis. Visas svetainės turinys atnaujinamas/papyldomasvartotojų ir turi aukštą kokybės standartą. Duomenų bazės ir svetainės yra atviro kodo pagal GPL standartą.</description>
     <description lang="mk">TheTVDB.com is a TV Scraper. The site is a massive open database that can be modified by anybody and contains full meta data for many shows in different languages. All content and images on the site have been contributed by their users for users and have a high standard or quality. The database schema and website are open source under the GPL.</description>
     <description lang="nl">TheTVDB.com is een seriescraper. De site is een gigantische open databank die iedereen kan aanpassen en uitbreiden, en biedt metadata aan voor TV-series in verschillende talen. Alle inhoud en afbeeldingen zijn afkomstig van gebruikers en moeten een grondige kwaliteitscontrole doorstaan. Het databankschema en de websitecode zijn vrijgegeven onder de open source GPL-licentie.</description>
-    <description lang="pl">TheTVDB.com jest scraperem TV. Ten serwis jest ogromną, wielojęzykową i otwartą bazą danych o serialach, którą może edytować każdy. Zawartość tego serwisu została dostarczona przez użytkowników, dla użytkowników. Dostępne materiały mają wysoki standard lub jakość.</description>
+    <description lang="pl">TheTVDB.com jest scraperem TV. Ten serwis jest ogromną, wielojęzykową i otwartą bazą danych o serialach, którą może edytować każdy. Zawartość tego serwisu została dostarczona przez użytkowników, dla użytkowników. Dostępne materiały są najwyższej jakości.</description>
     <description lang="pt">O TheTVDB.com é um scraper para séries TV. O site é uma gigantesca base de dados livre que pode ser alterada por qualquer pessoa e contém informação em várias línguas. Todos os conteúdos do site foram enviados por utilizadores e têm um elevado padrão de qualidade. A base de dados e o site funcionam sob uma licença GPL de software livre.</description>
-    <description lang="pt_BR">TheTVDB.com é um scraper de Seriados. O site é um enorme banco de dados aberto que pode ser modificado por qualquer pessoa e contém dados completos de muitos seriados em diferentes idiomas. Todos os conteúdos e imagens no site foram contribuições de usuários para usuários e têm um alto padrão de qualidade. O esquema do banco de dados e do site são de código aberto sob a licença GPL.</description>
+    <description lang="pt_BR">TheTVDB.com é um scraper de seriados. O site é um enorme banco de dados aberto que pode ser modificado por qualquer pessoa e contém metadados completos de muitos seriados em diferentes idiomas. Todos os conteúdos e imagens no site foram contribuições de usuários para usuários e têm um alto padrão de qualidade. O esquema do banco de dados e do site são de código aberto sob a licença GPL.</description>
     <description lang="ro">TheTVDB.com este un colectore TV. Site-ul este o bază de date masivă, deschisă, ce poate fi modificată de oricine şi conţine date complete despre multe seriale în diferite limbi. Tot conţinutul site-ului a fost contribuit de către utilizatori pentru utilizatori şi are un standard de caliate înalt. Schema bazei de date şi site-ul au sursele libere sub licenţă GPL.</description>
     <description lang="ru">TheTVDB.com — это инфоресурс для сериалов. Сайт представляет собой огромную открытую базу данных, в которую может вносить данные любой пользователь и которая содержит полные мета-данные для многих сериалов на различных языках. Всё содержимое и изображения на сайте были выложены его пользователями для других пользователей и имеют высокий уровень качества. Структура базы данных и веб-сайт созданы с открытым исходным кодом по лицензии GPL.</description>
     <description lang="se">TVDB.com är en TV-skrapa. Sajten är en massiv öppen databas som kan ändras av vem som helst och innehåller full metadata för mängder av serier på olika språk. Allt innehåll och bilder på sajten har bidragits av användare för användare och har en hög standard eller kvalitet. Databasschemat och webbsidan är licensierat som GPL.</description>
index 6327466..01a0e68 100644 (file)
@@ -1,3 +1,9 @@
+[B]1.4.5[/B]
+- Fixed: dvdorder setting won't apply in GetEpisodeDetails (thx and credits to scudlee)
+
+[B]1.4.4[/B]
+- Updated: language files from Transifex
+
 [B]1.4.3[/B]
 - Updated: language files from Transifex
 
diff --git a/addons/metadata.tvdb.com/resources/language/Amharic/strings.xml b/addons/metadata.tvdb.com/resources/language/Amharic/strings.xml
new file mode 100644 (file)
index 0000000..42c17dd
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Amharic language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/am/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30004">ቋንቋ </string>
+</strings>
index 8c6fee5..59cb6e8 100644 (file)
@@ -4,5 +4,9 @@
 <!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
 
 <strings>
+    <string id="30000">使用 DVD 順序</string>
+    <string id="30001">使用原本的順序 (單季)</string>
+    <string id="30002">啟用 Fanart</string>
+    <string id="30003">下載海報</string>
     <string id="30004">語言</string>
 </strings>
index 4747738..abba8d0 100644 (file)
@@ -6,7 +6,7 @@
 <strings>
     <string id="30000">Brug DVD rækkefølge</string>
     <string id="30001">Brug absolut rækkefølge (enkelt sæson)</string>
-    <string id="30002">Aktivér fankunst</string>
-    <string id="30003">Foretræk plakater</string>
+    <string id="30002">Aktiver Fankunst</string>
+    <string id="30003">Foretræk Plakater</string>
     <string id="30004">Sprog</string>
 </strings>
index a230852..200bc85 100644 (file)
@@ -4,5 +4,9 @@
 <!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
 
 <strings>
+    <string id="30000">Use DVD Order</string>
+    <string id="30001">Usar Oden Absoluto (Só unha tempada)</string>
+    <string id="30002">Habilitar Fanart</string>
+    <string id="30003">Preferir Carteis</string>
     <string id="30004">Idioma</string>
 </strings>
index da6137b..1ff93ec 100644 (file)
@@ -4,5 +4,7 @@
 <!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
 
 <strings>
+    <string id="30002">ファンアートを有効に</string>
+    <string id="30003">ポスターを使用</string>
     <string id="30004">言語</string>
 </strings>
diff --git a/addons/metadata.tvdb.com/resources/language/Malayalam/strings.xml b/addons/metadata.tvdb.com/resources/language/Malayalam/strings.xml
new file mode 100644 (file)
index 0000000..08dfeda
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<!-- Translated using Transifex web application. For support, or if you would like to to help out, please visit your language team! -->
+<!-- Malayalam language-Team URL: http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/ml/ -->
+<!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
+
+<strings>
+    <string id="30004">ഭാഷ</string>
+</strings>
index 1dee243..9b68cd8 100644 (file)
@@ -4,9 +4,9 @@
 <!-- Report language file syntax bugs at: http://trac.xbmc.org/ -->
 
 <strings>
-    <string id="30000">Usar Ordem DVD</string>
-    <string id="30001">Usar Ordenamento Absoluto (Temporada Única)</string>
+    <string id="30000">Usar a ordem do DVD</string>
+    <string id="30001">Usar ordenamento absoluto (Temporada Única)</string>
     <string id="30002">Ativar Fanart</string>
-    <string id="30003">Preferir Posters</string>
+    <string id="30003">Preferir Cartazes</string>
     <string id="30004">Idioma</string>
 </strings>
index 80b9c21..fa8d25a 100644 (file)
@@ -7,6 +7,6 @@
     <string id="30000">Usar ordem do DVD</string>
     <string id="30001">Usar Ordem Absoluta (Temporada única)</string>
     <string id="30002">Activar Fanart</string>
-    <string id="30003">Preferir pósters</string>
+    <string id="30003">Preferir posters</string>
     <string id="30004">Idioma</string>
 </strings>
index 5335f45..844f945 100644 (file)
                                <expression>&lt;EpisodeNumber&gt;([0-9]*)&lt;/EpisodeNumber&gt;.*?&lt;SeasonNumber&gt;0&lt;/SeasonNumber&gt;</expression>
                        </RegExp>
                        <!-- Normal Season/Episodes -->
-                       <RegExp conditional="!absolutenumber" input="$$8" output="&lt;season&gt;\1&lt;/season&gt;" dest="4+">
-                               <expression>&lt;SeasonNumber&gt;([^&lt;]*)&lt;/SeasonNumber&gt;</expression>
+                       <RegExp conditional="!absolutenumber" input="$$9" output="&lt;season&gt;\1&lt;/season&gt;" dest="4+">
+                               <RegExp conditional="!dvdorder" input="$$8" output="\1" dest="9">
+                                       <expression clear="yes">&lt;SeasonNumber&gt;([^&lt;]*)&lt;/SeasonNumber&gt;</expression>
+                               </RegExp>
+                               <RegExp conditional="dvdorder" input="$$8" output="\1" dest="9">
+                                       <expression>&lt;Combined_season&gt;([^&lt;]*)&lt;/Combined_season&gt;</expression>
+                               </RegExp>
+                               <expression/>
                        </RegExp>
-                       <RegExp conditional="!absolutenumber" input="$$8" output="&lt;episode&gt;\1&lt;/episode&gt;" dest="4+">
-                               <expression>&lt;EpisodeNumber&gt;([^&lt;]*)&lt;/EpisodeNumber&gt;</expression>
+                       <RegExp conditional="!absolutenumber" input="$$9" output="&lt;episode&gt;\1&lt;/episode&gt;" dest="4+">
+                               <RegExp conditional="!dvdorder" input="$$8" output="\1" dest="9">
+                                       <expression clear="yes">&lt;EpisodeNumber&gt;([^&lt;]*)&lt;/EpisodeNumber&gt;</expression>
+                               </RegExp>
+                               <RegExp conditional="dvdorder" input="$$8" output="\1" dest="9">
+                                       <expression>&lt;Combined_episodenumber&gt;([^&lt;]*)&lt;/Combined_episodenumber&gt;</expression>
+                               </RegExp>
+                               <expression/>
                        </RegExp>
                        <RegExp input="$$8" output="&lt;thumb&gt;http://thetvdb.com/banners/\1&lt;/thumb&gt;" dest="4+">
                                <expression>&lt;filename&gt;([^&lt;]+)&lt;/filename&gt;</expression>
index c9848fc..60e21ac 100644 (file)
@@ -6,7 +6,6 @@
                <control type="group">
                        <posx>720</posx>
                        <posy>0</posy>
-                       <animation effect="slide" end="-400,0" time="200" condition="Window.IsVisible(133)">conditional</animation>
                        <animation effect="slide" end="0,-80" time="200" condition="Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)">conditional</animation>
                        <control type="image">
                                <posx>0</posx>
@@ -45,4 +44,4 @@
                        </control>
                </control>
        </controls>
-</window>
\ No newline at end of file
+</window>
index f137ea2..2b567d9 100644 (file)
                                </control>
                        </control>
                </control>
-               <include>SideBladeRight</include>
                <include>Clock</include>
 
                <control type="label" id="20">
                        <visible>false</visible>
                </control>
        </controls>
-</window>
\ No newline at end of file
+</window>
index dd58296..36f016c 100644 (file)
                                </control>
                        </control>
                </control>
-               <include>SideBladeRight</include>
        </controls>
-</window>
\ No newline at end of file
+</window>
diff --git a/addons/skin.confluence/media/DefaultAddonPVRClient.png b/addons/skin.confluence/media/DefaultAddonPVRClient.png
new file mode 100644 (file)
index 0000000..b6ded67
Binary files /dev/null and b/addons/skin.confluence/media/DefaultAddonPVRClient.png differ
index 1d03746..932a728 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 1d03746646111f97e28b807a290bd762c7db547c
+Subproject commit 932a728725889c4220bc4ac42f18b082e4361f86
index a420d09..4bdb7e2 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="weather.wunderground" name="Weather Underground" version="1.0.8" provider-name="Team XBMC">
+<addon id="weather.wunderground" name="Weather Underground" version="1.0.9" provider-name="Team XBMC">
        <requires>
                <import addon="xbmc.python" version="2.1.0"/>
                <import addon="script.module.simplejson" version="2.0.10"/>
                <summary lang="no">Værvarsel fra wunderground.com</summary>
                <summary lang="pl">Prognoza pogody ze strony wunderground.com</summary>
                <summary lang="pt">Previsão meteorológica a partir de wunderground.com</summary>
-               <summary lang="pt_BR">Previsão Climática por wunderground.com </summary>
+               <summary lang="pt_BR">Previsão do tempo por wunderground.com</summary>
                <summary lang="ro">Starea vremii de la wunderground.com</summary>
                <summary lang="ru">Прогноз погоды с сайта wunderground.com</summary>
                <summary lang="sk">Predpoveď počasia z wunderground.com</summary>
                <summary lang="sl">Vremenska napoved, ki jo zagotavlja wunderground.com</summary>
                <summary lang="sv">Väderprognos från wunderground.com</summary>
                <summary lang="th">พยากรณ์อากาศจาก wunderground.com</summary>
+               <summary lang="tr">Hava durumu wunderground.com sitesinden alınır</summary>
                <summary lang="uk">Прогноз погоди з wunderground.com</summary>
                <summary lang="zh">来自wunderground.com的天气预报</summary>
                <summary lang="zh_TW">weatherground.com 上的天氣預報</summary>
                <description lang="no">Værvarsel fra Weather Underground (http://www.wunderground.com)</description>
                <description lang="pl">Prognoza pogody jest dostarczona dzięki uprzejmości serwisu: (http://www.wunderground.com/)</description>
                <description lang="pt">Previsão meteorológica fornecida por Weather Underground&#10;(http://www.wunderground.com/)</description>
-               <description lang="pt_BR">Previsão do clima por Weather Underground (http://www.wunderground.com/)</description>
+               <description lang="pt_BR">Previsão do tempo por Weather Underground (http://www.wunderground.com/)</description>
                <description lang="ro">Starea vremii furnizată de Weather Underground &#10;(http://www.wunderground.com/)</description>
                <description lang="ru">Прогноз погоды предоставлен компанией Weather Underground (http://www.wunderground.com/)</description>
                <description lang="sk">Predpoveď počasia poskytovaná od Weather Underground (http://www.wunderground.com/)</description>
                <description lang="sl">Vremenska napoved, ki jo zagotavlja Weather Underground (http://www.wunderground.com/)</description>
                <description lang="sv">Väderprognos tillhandahållen av Weather Underground&#10;(http://www.wunderground.com/)</description>
                <description lang="th">พยากรณ์อากาศโดย Weather Underground &#10;(http://www.wunderground.com/)</description>
+               <description lang="tr">Hava Durumu Weather Underground tarafından sağlanır (http://www.wunderground.com/)</description>
                <description lang="uk">Прогноз погоди надано Weather Underground (http://www.wunderground.com/)</description>
                <description lang="zh">由Weather Underground(http://www.wunderground.com/)提供的天气预报</description>
                <description lang="zh_TW">天氣預報由 Weather Underground (http://www.wunderground.com/) 所提供</description>
                <disclaimer lang="no">Bruken av dette tillegget betinger at du har godkjent brukervilkårene på http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
                <disclaimer lang="pl">Korzystanie z tego dodatku jest równoważne z akceptacją regulaminu dostępnego pod adresem: http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
                <disclaimer lang="pt">O uso deste Add-on implica o seu acordo com os Termos do Serviço. Pode encontrá-los em http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
-               <disclaimer lang="pt_BR">Usar este add-on implica que você concorda com os termos de uso que podem ser visualizados em http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
+               <disclaimer lang="pt_BR">Usar este add-on implica que você concorda com os termos de uso localizados em http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
                <disclaimer lang="ro">Folosirea acestui plugin implică acceptarea Termenilor și Serviciilor aflate la adresa http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
                <disclaimer lang="ru">Используя это дополнение, вы соглашаетесь с условиями предоставления услуг, доступными по адресу http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
-               <disclaimer lang="sk">Používanie tohto add-onu znamená, že súhlasite s podmienkami používania služby umiestnenými na  http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
+               <disclaimer lang="sk">Používaním tohto doplnku súhlasíte s podmienkami používania služby umiestnenými na http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
                <disclaimer lang="sl">Z uporabo vtičnika se strinjate s Pogoji uporabe, ki se nahajajo na http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
                <disclaimer lang="sv">Användning av detta tillägg innebär att ni har accepterat användarvillkoren som går att hitta på&#10;http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
                <disclaimer lang="th">ในการใช้บริการโปรแกรมเสริมนี้หมายความว่าคุณได้ยอมรับเงื่อนไขในการให้บริการโดย http://www.wunderground.com/weather/api/d/terms.html</disclaimer>
index 72886ca..a7193a7 100644 (file)
@@ -1,3 +1,6 @@
+v1.0.9
+- updated language files from Transifex
+
 v1.0.8
 - language update
 
index bbd20f9..8151005 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
diff --git a/addons/weather.wunderground/resources/language/Amharic/strings.po b/addons/weather.wunderground/resources/language/Amharic/strings.po
new file mode 100644 (file)
index 0000000..f3d7ee0
--- /dev/null
@@ -0,0 +1,93 @@
+# XBMC Media Center language file
+# Addon Name: Weather Underground
+# Addon id: weather.wunderground
+# Addon Provider: Team XBMC
+msgid ""
+msgstr ""
+"Project-Id-Version: XBMC Main Translation Project (Frodo)\n"
+"Report-Msgid-Bugs-To: http://trac.xbmc.org/\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: XBMC Translation Team\n"
+"Language-Team: Amharic (http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/am/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: am\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+msgctxt "#32101"
+msgid "Location setup"
+msgstr "አካባቢ ማሰናጃ "
+
+msgctxt "#32102"
+msgid "Advanced options"
+msgstr "የረቀቁ ምርጫዎች"
+
+msgctxt "#32111"
+msgid "Location 1"
+msgstr "አካባቢ 1 "
+
+msgctxt "#32112"
+msgid "Location 2"
+msgstr "አካባቢ 2 "
+
+msgctxt "#32113"
+msgid "Location 3"
+msgstr "አካባቢ 3 "
+
+msgctxt "#32114"
+msgid "Location 1 display name"
+msgstr "አካባቢ 1 ስም ማሳያ "
+
+msgctxt "#32115"
+msgid "Location 2 display name"
+msgstr "አካባቢ 2 ስም ማሳያ"
+
+msgctxt "#32116"
+msgid "Location 3 display name"
+msgstr "አካባቢ 3 ስም ማሳያ"
+
+msgctxt "#32117"
+msgid "Location 1 id"
+msgstr "አካባቢ 1 መለያ "
+
+msgctxt "#32118"
+msgid "Location 2 id"
+msgstr "አካባቢ 2 መለያ"
+
+msgctxt "#32119"
+msgid "Location 3 id"
+msgstr "አካባቢ 3 መለያ"
+
+msgctxt "#32121"
+msgid "Weekend"
+msgstr "የሳምንቱ መጨረሻ"
+
+msgctxt "#32122"
+msgid "Saturday/Sunday"
+msgstr "ቅዳሜ / እሑድ "
+
+msgctxt "#32123"
+msgid "Friday/Saturday"
+msgstr "አርብ / ቅዳሜ "
+
+msgctxt "#32124"
+msgid "Thursday/Friday"
+msgstr "ሐሙስ / አርብ "
+
+msgctxt "#32501"
+msgid "New Moon"
+msgstr "አዲስ ጨረቃ "
+
+msgctxt "#32505"
+msgid "Full Moon"
+msgstr "ሙሉ ጨረቃ "
+
+msgctxt "#32510"
+msgid "Warning"
+msgstr "ማስጠንቀቂያ "
+
+msgctxt "#32515"
+msgid "Forecast"
+msgstr "ግምት "
index 01f3d45..a9184a3 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 034b6d1..0dc676f 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 67b8753..8a3e954 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 3ec447a..0e3f0ad 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 16ab0d0..95cf00f 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index b5cdbc5..a42819c 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 9967a13..dc37d9d 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index e2f466c..79a12d2 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -125,6 +124,18 @@ msgctxt "#32510"
 msgid "Warning"
 msgstr "警告"
 
+msgctxt "#32511"
+msgid "Watch"
+msgstr "觀看"
+
+msgctxt "#32512"
+msgid "Advisory"
+msgstr "諮詢"
+
+msgctxt "#32513"
+msgid "Statement"
+msgstr "聲明"
+
 msgctxt "#32514"
 msgid "Outlook"
 msgstr "氣象趨勢"
index 1182257..48c9f85 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -17,10 +16,126 @@ msgstr ""
 "Language: hr\n"
 "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 
+msgctxt "#32101"
+msgid "Location setup"
+msgstr "Namjesti lokaciju"
+
+msgctxt "#32102"
+msgid "Advanced options"
+msgstr "Dodatne opcije"
+
+msgctxt "#32111"
+msgid "Location 1"
+msgstr "Lokacija 1"
+
+msgctxt "#32112"
+msgid "Location 2"
+msgstr "Lokacija 2"
+
+msgctxt "#32113"
+msgid "Location 3"
+msgstr "Lokacija 3"
+
+msgctxt "#32114"
+msgid "Location 1 display name"
+msgstr "Lokacija 1 ime ekrana"
+
+msgctxt "#32115"
+msgid "Location 2 display name"
+msgstr "Lokacija 2 ime ekrana"
+
+msgctxt "#32116"
+msgid "Location 3 display name"
+msgstr "Lokacija 3 ime ekrana"
+
+msgctxt "#32117"
+msgid "Location 1 id"
+msgstr "Lokacija 1 id"
+
+msgctxt "#32118"
+msgid "Location 2 id"
+msgstr "Lokacija 2 id"
+
+msgctxt "#32119"
+msgid "Location 3 id"
+msgstr "Lokacija 3 id"
+
+msgctxt "#32120"
+msgid "Enable logging"
+msgstr "Omogućiti prijave"
+
 msgctxt "#32121"
 msgid "Weekend"
 msgstr "Vikend"
 
+msgctxt "#32122"
+msgid "Saturday/Sunday"
+msgstr "Subota/Nedjelja"
+
+msgctxt "#32123"
+msgid "Friday/Saturday"
+msgstr "Petak/Subota"
+
+msgctxt "#32124"
+msgid "Thursday/Friday"
+msgstr "Četvrtak/Petak"
+
+msgctxt "#32125"
+msgid "Map zoom level"
+msgstr "Razina zumiranja karte"
+
+msgctxt "#32126"
+msgid "Animated map"
+msgstr "Animirane mape"
+
+msgctxt "#32501"
+msgid "New Moon"
+msgstr "Mladi Mjesec"
+
+msgctxt "#32502"
+msgid "Waxing Crescent"
+msgstr "Prva četvrt"
+
+msgctxt "#32503"
+msgid "First Quarter"
+msgstr "Prva četvrtina"
+
+msgctxt "#32504"
+msgid "Waxing Gibbous"
+msgstr "Prva četvrt"
+
+msgctxt "#32505"
+msgid "Full Moon"
+msgstr "Puni Mjesec"
+
+msgctxt "#32506"
+msgid "Waning Gibbous"
+msgstr "Zadnja četvrt"
+
+msgctxt "#32507"
+msgid "Last Quarter"
+msgstr "Posljednja četvrtina"
+
+msgctxt "#32508"
+msgid "Waning Crescent"
+msgstr "Mlađak"
+
+msgctxt "#32510"
+msgid "Warning"
+msgstr "Obavjesti"
+
+msgctxt "#32511"
+msgid "Watch"
+msgstr "Bdjeti"
+
+msgctxt "#32512"
+msgid "Advisory"
+msgstr "Savjetnik"
+
+msgctxt "#32513"
+msgid "Statement"
+msgstr "Izvještaj"
+
 msgctxt "#32514"
 msgid "Outlook"
 msgstr "Izgledi"
@@ -28,3 +143,7 @@ msgstr "Izgledi"
 msgctxt "#32515"
 msgid "Forecast"
 msgstr "Prognoza"
+
+msgctxt "#32516"
+msgid "Synopsis"
+msgstr "Kratki sadržaj"
index 9fe60d6..2cf2d7b 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 8b7bae1..0330826 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 1d55dec..84046fb 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 5dedd9d..3f243c8 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 5f5e89c..b3e87ae 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -17,14 +16,126 @@ msgstr ""
 "Language: et\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+msgctxt "#32101"
+msgid "Location setup"
+msgstr "Asukoha sätted"
+
+msgctxt "#32102"
+msgid "Advanced options"
+msgstr "Täpsemad seaded"
+
+msgctxt "#32111"
+msgid "Location 1"
+msgstr "Asukoht 1"
+
+msgctxt "#32112"
+msgid "Location 2"
+msgstr "Asukoht 2"
+
+msgctxt "#32113"
+msgid "Location 3"
+msgstr "Asukoht 3"
+
+msgctxt "#32114"
+msgid "Location 1 display name"
+msgstr "Asukoht 1 kuvatav nimi"
+
+msgctxt "#32115"
+msgid "Location 2 display name"
+msgstr "Asukoht 2 kuvatav nimi"
+
+msgctxt "#32116"
+msgid "Location 3 display name"
+msgstr "Asukoht 3 kuvatav nimi"
+
+msgctxt "#32117"
+msgid "Location 1 id"
+msgstr "Asukoht 1 id"
+
+msgctxt "#32118"
+msgid "Location 2 id"
+msgstr "Asukoht 2 id"
+
+msgctxt "#32119"
+msgid "Location 3 id"
+msgstr "Asukoht 3 id"
+
+msgctxt "#32120"
+msgid "Enable logging"
+msgstr "Luba logimine"
+
 msgctxt "#32121"
 msgid "Weekend"
 msgstr "Nädalavahetus"
 
+msgctxt "#32122"
+msgid "Saturday/Sunday"
+msgstr "Laupäev/Pühapäev"
+
+msgctxt "#32123"
+msgid "Friday/Saturday"
+msgstr "Reede/Laupäev"
+
+msgctxt "#32124"
+msgid "Thursday/Friday"
+msgstr "Neljapäev/Reede"
+
+msgctxt "#32125"
+msgid "Map zoom level"
+msgstr "Kaardi suurenduse tase"
+
+msgctxt "#32126"
+msgid "Animated map"
+msgstr "Animeeritud kaart"
+
+msgctxt "#32501"
+msgid "New Moon"
+msgstr "Noorkuu"
+
+msgctxt "#32502"
+msgid "Waxing Crescent"
+msgstr "Viimane veerand"
+
+msgctxt "#32503"
+msgid "First Quarter"
+msgstr "Esimene veerand"
+
+msgctxt "#32504"
+msgid "Waxing Gibbous"
+msgstr "Kasvav kuu"
+
+msgctxt "#32505"
+msgid "Full Moon"
+msgstr "Täiskuu"
+
+msgctxt "#32506"
+msgid "Waning Gibbous"
+msgstr "Kahanev kuu"
+
+msgctxt "#32507"
+msgid "Last Quarter"
+msgstr "Viimane veerand"
+
+msgctxt "#32508"
+msgid "Waning Crescent"
+msgstr "Viimane veerand"
+
 msgctxt "#32510"
 msgid "Warning"
 msgstr "Hoiatus"
 
+msgctxt "#32511"
+msgid "Watch"
+msgstr "Vaata"
+
+msgctxt "#32512"
+msgid "Advisory"
+msgstr "Nõuandev"
+
+msgctxt "#32513"
+msgid "Statement"
+msgstr "Avaldus"
+
 msgctxt "#32514"
 msgid "Outlook"
 msgstr "Väljavaade"
@@ -32,3 +143,7 @@ msgstr "Väljavaade"
 msgctxt "#32515"
 msgid "Forecast"
 msgstr "Ilmateade"
+
+msgctxt "#32516"
+msgid "Synopsis"
+msgstr "Ülevaade"
index 1106a6a..a43cb42 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 73b117e..3faa1ca 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 923d6e8..c2b0498 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -17,14 +16,126 @@ msgstr ""
 "Language: gl\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+msgctxt "#32101"
+msgid "Location setup"
+msgstr "Configuración da localización"
+
+msgctxt "#32102"
+msgid "Advanced options"
+msgstr "Opcións Avanzadas"
+
+msgctxt "#32111"
+msgid "Location 1"
+msgstr "Localización 1"
+
+msgctxt "#32112"
+msgid "Location 2"
+msgstr "Localización 2"
+
+msgctxt "#32113"
+msgid "Location 3"
+msgstr "Localización 3"
+
+msgctxt "#32114"
+msgid "Location 1 display name"
+msgstr "Nome a mostrar da localización 1"
+
+msgctxt "#32115"
+msgid "Location 2 display name"
+msgstr "Nome a mostrar da localización 2"
+
+msgctxt "#32116"
+msgid "Location 3 display name"
+msgstr "Nome a mostrar da localización 3"
+
+msgctxt "#32117"
+msgid "Location 1 id"
+msgstr "ID da localización 1"
+
+msgctxt "#32118"
+msgid "Location 2 id"
+msgstr "ID da localización 2"
+
+msgctxt "#32119"
+msgid "Location 3 id"
+msgstr "ID da localización 3"
+
+msgctxt "#32120"
+msgid "Enable logging"
+msgstr "Habilitar o rexistro"
+
 msgctxt "#32121"
 msgid "Weekend"
 msgstr "Fin de semana"
 
+msgctxt "#32122"
+msgid "Saturday/Sunday"
+msgstr "Sábado/Domingo"
+
+msgctxt "#32123"
+msgid "Friday/Saturday"
+msgstr "Venres/Sábado"
+
+msgctxt "#32124"
+msgid "Thursday/Friday"
+msgstr "Xoves/Venres"
+
+msgctxt "#32125"
+msgid "Map zoom level"
+msgstr "Nivel de aumento do mapa"
+
+msgctxt "#32126"
+msgid "Animated map"
+msgstr "Mapa animado"
+
+msgctxt "#32501"
+msgid "New Moon"
+msgstr "Lúa Nova"
+
+msgctxt "#32502"
+msgid "Waxing Crescent"
+msgstr "Lúa Nova Visible"
+
+msgctxt "#32503"
+msgid "First Quarter"
+msgstr "Cuarto Crecente"
+
+msgctxt "#32504"
+msgid "Waxing Gibbous"
+msgstr "Gibosa Crecente"
+
+msgctxt "#32505"
+msgid "Full Moon"
+msgstr "Lúa Chea"
+
+msgctxt "#32506"
+msgid "Waning Gibbous"
+msgstr "Gibosa Minguante"
+
+msgctxt "#32507"
+msgid "Last Quarter"
+msgstr "Cuarto Minguante"
+
+msgctxt "#32508"
+msgid "Waning Crescent"
+msgstr "Lúa Minguante"
+
 msgctxt "#32510"
 msgid "Warning"
 msgstr "Atención"
 
+msgctxt "#32511"
+msgid "Watch"
+msgstr "Vixiar"
+
+msgctxt "#32512"
+msgid "Advisory"
+msgstr "Consello"
+
+msgctxt "#32513"
+msgid "Statement"
+msgstr "Declaración"
+
 msgctxt "#32514"
 msgid "Outlook"
 msgstr "Perspectiva"
@@ -32,3 +143,7 @@ msgstr "Perspectiva"
 msgctxt "#32515"
 msgid "Forecast"
 msgstr "Predición"
+
+msgctxt "#32516"
+msgid "Synopsis"
+msgstr "Sinopse"
index 4a86802..4ce0263 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index dacdd13..04638a5 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 588efa5..27bf7ef 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index d224a90..bbf6cc6 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 803ebf5..4ae45b2 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index c63e203..4995483 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 5a1fe83..2bbbca2 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 15b18cf..74ca64b 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -99,23 +98,15 @@ msgstr "초승달"
 
 msgctxt "#32503"
 msgid "First Quarter"
-msgstr "반달"
-
-msgctxt "#32504"
-msgid "Waxing Gibbous"
 msgstr "상현달"
 
 msgctxt "#32505"
 msgid "Full Moon"
 msgstr "보름달"
 
-msgctxt "#32506"
-msgid "Waning Gibbous"
-msgstr "하현달"
-
 msgctxt "#32507"
 msgid "Last Quarter"
-msgstr "달"
+msgstr "하현달"
 
 msgctxt "#32508"
 msgid "Waning Crescent"
index 384dbdd..bf26589 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 9e2b3c2..60297a5 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 5563465..1511850 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -17,18 +16,134 @@ msgstr ""
 "Language: no\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+msgctxt "#32101"
+msgid "Location setup"
+msgstr "Steder"
+
+msgctxt "#32102"
+msgid "Advanced options"
+msgstr "Avanserte alternativer"
+
+msgctxt "#32111"
+msgid "Location 1"
+msgstr "Sted 1"
+
+msgctxt "#32112"
+msgid "Location 2"
+msgstr "Sted 2"
+
+msgctxt "#32113"
+msgid "Location 3"
+msgstr "Sted 3"
+
+msgctxt "#32114"
+msgid "Location 1 display name"
+msgstr "Visningsnavn for sted 1"
+
+msgctxt "#32115"
+msgid "Location 2 display name"
+msgstr "Visningsnavn for sted 2"
+
+msgctxt "#32116"
+msgid "Location 3 display name"
+msgstr "Visningsnavn for sted 3"
+
+msgctxt "#32117"
+msgid "Location 1 id"
+msgstr "ID for sted 1"
+
+msgctxt "#32118"
+msgid "Location 2 id"
+msgstr "ID for sted 2"
+
+msgctxt "#32119"
+msgid "Location 3 id"
+msgstr "ID for sted 3"
+
+msgctxt "#32120"
+msgid "Enable logging"
+msgstr "Aktiver logging"
+
 msgctxt "#32121"
 msgid "Weekend"
 msgstr "Helg"
 
+msgctxt "#32122"
+msgid "Saturday/Sunday"
+msgstr "Lørdag/søndag"
+
+msgctxt "#32123"
+msgid "Friday/Saturday"
+msgstr "Fredag/lørdag"
+
+msgctxt "#32124"
+msgid "Thursday/Friday"
+msgstr "Torsdag/fredag"
+
+msgctxt "#32125"
+msgid "Map zoom level"
+msgstr "Zoomnivå for kart"
+
+msgctxt "#32126"
+msgid "Animated map"
+msgstr "Animert kart"
+
+msgctxt "#32501"
+msgid "New Moon"
+msgstr "Nymåne"
+
+msgctxt "#32502"
+msgid "Waxing Crescent"
+msgstr "Waxing Crescent"
+
+msgctxt "#32503"
+msgid "First Quarter"
+msgstr "Halvmåne ny"
+
+msgctxt "#32504"
+msgid "Waxing Gibbous"
+msgstr "Tiltagende mot fullmåne"
+
+msgctxt "#32505"
+msgid "Full Moon"
+msgstr "Fullmåne"
+
+msgctxt "#32506"
+msgid "Waning Gibbous"
+msgstr "Minkende fra fullmåne"
+
+msgctxt "#32507"
+msgid "Last Quarter"
+msgstr "Halvmåne ne"
+
+msgctxt "#32508"
+msgid "Waning Crescent"
+msgstr "Minkende fra fullmåne"
+
 msgctxt "#32510"
 msgid "Warning"
 msgstr "Advarsel"
 
+msgctxt "#32511"
+msgid "Watch"
+msgstr "Se"
+
+msgctxt "#32512"
+msgid "Advisory"
+msgstr "Anbefaling"
+
+msgctxt "#32513"
+msgid "Statement"
+msgstr "Uttalelse"
+
 msgctxt "#32514"
 msgid "Outlook"
-msgstr "Outlook"
+msgstr "Prognose"
 
 msgctxt "#32515"
 msgid "Forecast"
 msgstr "Værvarsel"
+
+msgctxt "#32516"
+msgid "Synopsis"
+msgstr "Sammendrag"
diff --git a/addons/weather.wunderground/resources/language/Persian/strings.po b/addons/weather.wunderground/resources/language/Persian/strings.po
new file mode 100644 (file)
index 0000000..60b9268
--- /dev/null
@@ -0,0 +1,45 @@
+# XBMC Media Center language file
+# Addon Name: Weather Underground
+# Addon id: weather.wunderground
+# Addon Provider: Team XBMC
+msgid ""
+msgstr ""
+"Project-Id-Version: XBMC Main Translation Project (Frodo)\n"
+"Report-Msgid-Bugs-To: http://trac.xbmc.org/\n"
+"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: XBMC Translation Team\n"
+"Language-Team: Persian (http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/fa/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: fa\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "#32111"
+msgid "Location 1"
+msgstr "موقعیت 1"
+
+msgctxt "#32112"
+msgid "Location 2"
+msgstr "موقعیت 2"
+
+msgctxt "#32113"
+msgid "Location 3"
+msgstr "موقعیت 3"
+
+msgctxt "#32114"
+msgid "Location 1 display name"
+msgstr "نام مستعار موقیت 1"
+
+msgctxt "#32115"
+msgid "Location 2 display name"
+msgstr "نام مستعار موقعیت 2"
+
+msgctxt "#32116"
+msgid "Location 3 display name"
+msgstr "نام مستعار موقعیت 3"
+
+msgctxt "#32121"
+msgid "Weekend"
+msgstr "آخر هفته"
index b4e048c..e9a94c5 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 69370ab..6faf71b 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -27,43 +26,43 @@ msgstr "Opções Avançadas"
 
 msgctxt "#32111"
 msgid "Location 1"
-msgstr "Cidade 1"
+msgstr "Local 1"
 
 msgctxt "#32112"
 msgid "Location 2"
-msgstr "Cidade 2"
+msgstr "Local 2"
 
 msgctxt "#32113"
 msgid "Location 3"
-msgstr "Cidade 3"
+msgstr "Local 3"
 
 msgctxt "#32114"
 msgid "Location 1 display name"
-msgstr "Nome da Cidade"
+msgstr "Nome de exibição do local 1"
 
 msgctxt "#32115"
 msgid "Location 2 display name"
-msgstr "Nome da Cidade"
+msgstr "Nome de exibição do local 2"
 
 msgctxt "#32116"
 msgid "Location 3 display name"
-msgstr "Nome da Cidade"
+msgstr "Nome de exibição do local 3"
 
 msgctxt "#32117"
 msgid "Location 1 id"
-msgstr "Local 1 id"
+msgstr "ID do local 1"
 
 msgctxt "#32118"
 msgid "Location 2 id"
-msgstr "Local 2 id"
+msgstr "ID do local 2"
 
 msgctxt "#32119"
 msgid "Location 3 id"
-msgstr "Local 3 id"
+msgstr "ID do local 3"
 
 msgctxt "#32120"
 msgid "Enable logging"
-msgstr "Ativar logando"
+msgstr "Ativar registro"
 
 msgctxt "#32121"
 msgid "Weekend"
@@ -79,7 +78,7 @@ msgstr "Sexta/Sábado"
 
 msgctxt "#32124"
 msgid "Thursday/Friday"
-msgstr "Quarta/Quinta"
+msgstr "Quinta/Sexta"
 
 msgctxt "#32125"
 msgid "Map zoom level"
@@ -87,7 +86,7 @@ msgstr "Nível de zoom no mapa"
 
 msgctxt "#32126"
 msgid "Animated map"
-msgstr "Mapa Animado"
+msgstr "Mapa animado"
 
 msgctxt "#32501"
 msgid "New Moon"
@@ -95,15 +94,15 @@ msgstr "Lua Nova"
 
 msgctxt "#32502"
 msgid "Waxing Crescent"
-msgstr "Quarta Crescente"
+msgstr "Lua Crescente"
 
 msgctxt "#32503"
 msgid "First Quarter"
-msgstr "Lua Nova"
+msgstr "Quarto Crescente"
 
 msgctxt "#32504"
 msgid "Waxing Gibbous"
-msgstr "Quarta Minguante"
+msgstr "Lua Gibosa"
 
 msgctxt "#32505"
 msgid "Full Moon"
@@ -111,15 +110,15 @@ msgstr "Lua Cheia"
 
 msgctxt "#32506"
 msgid "Waning Gibbous"
-msgstr "Minguante"
+msgstr "Lua Balsâmica"
 
 msgctxt "#32507"
 msgid "Last Quarter"
-msgstr "Lua Minguante"
+msgstr "Quarto Minguante"
 
 msgctxt "#32508"
 msgid "Waning Crescent"
-msgstr "Lua Crescente"
+msgstr "Lua Minguante"
 
 msgctxt "#32510"
 msgid "Warning"
index 2b8ff2c..8ab99a9 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 6e26711..d1cd018 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 08ebb79..0277394 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 9dd417e..500dc83 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index b0937ee..42b0424 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index e811116..67864ca 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -17,14 +16,126 @@ msgstr ""
 "Language: sk\n"
 "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
 
+msgctxt "#32101"
+msgid "Location setup"
+msgstr "Nastavenie Polohy"
+
+msgctxt "#32102"
+msgid "Advanced options"
+msgstr "Pokročilé nastavenia"
+
+msgctxt "#32111"
+msgid "Location 1"
+msgstr "Poloha 1"
+
+msgctxt "#32112"
+msgid "Location 2"
+msgstr "Poloha 2"
+
+msgctxt "#32113"
+msgid "Location 3"
+msgstr "Poloha 3"
+
+msgctxt "#32114"
+msgid "Location 1 display name"
+msgstr "Pomenovanie Polohy 1"
+
+msgctxt "#32115"
+msgid "Location 2 display name"
+msgstr "Pomenovanie Polohy 2"
+
+msgctxt "#32116"
+msgid "Location 3 display name"
+msgstr "Pomenovanie Polohy 3"
+
+msgctxt "#32117"
+msgid "Location 1 id"
+msgstr "ID Polohy 1"
+
+msgctxt "#32118"
+msgid "Location 2 id"
+msgstr "ID Polohy 2"
+
+msgctxt "#32119"
+msgid "Location 3 id"
+msgstr "ID Polohy 3"
+
+msgctxt "#32120"
+msgid "Enable logging"
+msgstr "Povoliť logovanie"
+
 msgctxt "#32121"
 msgid "Weekend"
 msgstr "Víkend"
 
+msgctxt "#32122"
+msgid "Saturday/Sunday"
+msgstr "Sobota/Nedeľa"
+
+msgctxt "#32123"
+msgid "Friday/Saturday"
+msgstr "Piatok/Sobota"
+
+msgctxt "#32124"
+msgid "Thursday/Friday"
+msgstr "Štvrtok/Piatok"
+
+msgctxt "#32125"
+msgid "Map zoom level"
+msgstr "Úroveň priblíženia mapy"
+
+msgctxt "#32126"
+msgid "Animated map"
+msgstr "Animovaná mapa"
+
+msgctxt "#32501"
+msgid "New Moon"
+msgstr "Nov mesiaca"
+
+msgctxt "#32502"
+msgid "Waxing Crescent"
+msgstr "Dorastajúci kosák"
+
+msgctxt "#32503"
+msgid "First Quarter"
+msgstr "Prvá štvrť"
+
+msgctxt "#32504"
+msgid "Waxing Gibbous"
+msgstr "Dorastajúci mesiac"
+
+msgctxt "#32505"
+msgid "Full Moon"
+msgstr "Spln mesiaca"
+
+msgctxt "#32506"
+msgid "Waning Gibbous"
+msgstr "Cúvajúci mesiac"
+
+msgctxt "#32507"
+msgid "Last Quarter"
+msgstr "Posledná štvrť"
+
+msgctxt "#32508"
+msgid "Waning Crescent"
+msgstr "Ubúdajúci kosák"
+
 msgctxt "#32510"
 msgid "Warning"
 msgstr "Upozornenie"
 
+msgctxt "#32511"
+msgid "Watch"
+msgstr "Výstraha"
+
+msgctxt "#32512"
+msgid "Advisory"
+msgstr "Informačné spravodajstvo"
+
+msgctxt "#32513"
+msgid "Statement"
+msgstr "Oznámenie"
+
 msgctxt "#32514"
 msgid "Outlook"
 msgstr "Výhľad"
@@ -32,3 +143,7 @@ msgstr "Výhľad"
 msgctxt "#32515"
 msgid "Forecast"
 msgstr "Predpoveď"
+
+msgctxt "#32516"
+msgid "Synopsis"
+msgstr "Zhrnutie"
index dc17b51..8845a02 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 6bdb9db..1243189 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 87abc7d..a1cd15f 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -17,10 +16,126 @@ msgstr ""
 "Language: es_MX\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
+msgctxt "#32101"
+msgid "Location setup"
+msgstr "Configurar Ubicación"
+
+msgctxt "#32102"
+msgid "Advanced options"
+msgstr "Opciones Avanzadas"
+
+msgctxt "#32111"
+msgid "Location 1"
+msgstr "Ubicación 1"
+
+msgctxt "#32112"
+msgid "Location 2"
+msgstr "Ubicación 2"
+
+msgctxt "#32113"
+msgid "Location 3"
+msgstr "Ubicación 3"
+
+msgctxt "#32114"
+msgid "Location 1 display name"
+msgstr "Ubicación 1 Mostrar Nombre"
+
+msgctxt "#32115"
+msgid "Location 2 display name"
+msgstr "Ubicación 2 Mostrar Nombre"
+
+msgctxt "#32116"
+msgid "Location 3 display name"
+msgstr "Ubicación 3 Mostrar Nombre"
+
+msgctxt "#32117"
+msgid "Location 1 id"
+msgstr "Ubicación 1 id"
+
+msgctxt "#32118"
+msgid "Location 2 id"
+msgstr "Ubicación 2 id"
+
+msgctxt "#32119"
+msgid "Location 3 id"
+msgstr "Ubicación 3 id"
+
+msgctxt "#32120"
+msgid "Enable logging"
+msgstr "Habilitar logs"
+
 msgctxt "#32121"
 msgid "Weekend"
 msgstr "Fin de semana"
 
+msgctxt "#32122"
+msgid "Saturday/Sunday"
+msgstr "Sábado/Domingo"
+
+msgctxt "#32123"
+msgid "Friday/Saturday"
+msgstr "Viernes/Sábado"
+
+msgctxt "#32124"
+msgid "Thursday/Friday"
+msgstr "Jueves/Viernes"
+
+msgctxt "#32125"
+msgid "Map zoom level"
+msgstr "Nivel de Acercamiento en Mapa"
+
+msgctxt "#32126"
+msgid "Animated map"
+msgstr "Mapa Animado"
+
+msgctxt "#32501"
+msgid "New Moon"
+msgstr "Luna nueva"
+
+msgctxt "#32502"
+msgid "Waxing Crescent"
+msgstr "Luna Creciente"
+
+msgctxt "#32503"
+msgid "First Quarter"
+msgstr "Primer Cuarto"
+
+msgctxt "#32504"
+msgid "Waxing Gibbous"
+msgstr "Gibosa Iluminarte"
+
+msgctxt "#32505"
+msgid "Full Moon"
+msgstr "Luna Llena"
+
+msgctxt "#32506"
+msgid "Waning Gibbous"
+msgstr "Luna Menguante"
+
+msgctxt "#32507"
+msgid "Last Quarter"
+msgstr "Último cuarto"
+
+msgctxt "#32508"
+msgid "Waning Crescent"
+msgstr "Creciente Menguante"
+
+msgctxt "#32510"
+msgid "Warning"
+msgstr "Alerta"
+
+msgctxt "#32511"
+msgid "Watch"
+msgstr "Reloj"
+
+msgctxt "#32512"
+msgid "Advisory"
+msgstr "Advertencia"
+
+msgctxt "#32513"
+msgid "Statement"
+msgstr "Sentencia"
+
 msgctxt "#32514"
 msgid "Outlook"
 msgstr "Outlook"
@@ -28,3 +143,7 @@ msgstr "Outlook"
 msgctxt "#32515"
 msgid "Forecast"
 msgstr "Pronóstico"
+
+msgctxt "#32516"
+msgid "Synopsis"
+msgstr "Sinopsis"
index 47602f4..692ec23 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index fb4f1a1..866359c 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 39d3591..57ca455 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index 60a992d..4a26db5 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
@@ -121,6 +120,10 @@ msgctxt "#32510"
 msgid "Warning"
 msgstr "Uyarı"
 
+msgctxt "#32511"
+msgid "Watch"
+msgstr "İzle"
+
 msgctxt "#32514"
 msgid "Outlook"
 msgstr "Outlook"
index f1fa886..382b111 100644 (file)
@@ -1,7 +1,6 @@
 # XBMC Media Center language file
 # Addon Name: Weather Underground
 # Addon id: weather.wunderground
-# Addon version: 1.0.8
 # Addon Provider: Team XBMC
 msgid ""
 msgstr ""
index e4aad8c..41434df 100644 (file)
@@ -24,7 +24,7 @@ AC_CONFIG_FILES( \
   test/Makefile \
   tools/Makefile)
 
-AM_CONFIG_HEADER(config.h)
+AC_CONFIG_HEADERS(config.h)
 AM_INIT_AUTOMAKE([1.8 gnits check-news dist-bzip2])
 AM_ACLOCAL_INCLUDE(m4)
 AM_MAINTAINER_MODE
@@ -40,7 +40,6 @@ AC_GNU_SOURCE
 AC_AIX
 AC_ISC_POSIX
 AC_PROG_LIBTOOL
-AM_PROG_CC_STDC
 AM_PROG_CC_C_O
 AC_PROG_INSTALL
 AC_PROG_LN_S
index 3856001..f4d793d 100644 (file)
@@ -723,11 +723,6 @@ PLT_MediaItem::FromDidl(NPT_XmlElementNode* entry)
     }
 
     NPT_Result result = PLT_MediaObject::FromDidl(entry);
-    
-    // make sure we have at least one valid resource
-    if (m_Resources.GetItemCount() == 0) {
-        NPT_CHECK_SEVERE(NPT_ERROR_INVALID_PARAMETERS);
-    }
 
     return result;
 }
index 1b9ff8b..a40a161 100644 (file)
@@ -5,7 +5,7 @@ AC_CONFIG_AUX_DIR(.auto)
 AC_CANONICAL_SYSTEM
 
 AM_INIT_AUTOMAKE(libdvdcss, 1.2.10)
-AM_CONFIG_HEADER(config.h)
+AC_CONFIG_HEADERS(config.h)
 
 AC_PROG_CC
 AC_STDC_HEADERS
index bc19d8e..e137ca6 100644 (file)
@@ -28,7 +28,7 @@ AC_CONFIG_SRCDIR([decoder.h])
 
 AM_INIT_AUTOMAKE
 
-AM_CONFIG_HEADER([config.h])
+AC_CONFIG_HEADERS([config.h])
 
 dnl System type.
 
index b314357..c00e55b 100644 (file)
@@ -23,7 +23,7 @@
 AC_PREREQ(2.57)
 AC_INIT([libmicrohttpd], [0.4.5],[libmicrohttpd@gnu.org])
 AM_INIT_AUTOMAKE([libmicrohttpd], [0.4.5])
-AM_CONFIG_HEADER([MHD_config.h])
+AC_CONFIG_HEADERS([MHD_config.h])
 AC_CONFIG_MACRO_DIR([m4])
 AH_TOP([#define _GNU_SOURCE  1])
 
index e4ad8ca..bd67f5f 100644 (file)
@@ -9,7 +9,7 @@ SET DEPS_DIR=..\BuildDependencies
 SET TMP_DIR=%DEPS_DIR%\tmp
 
 SET LIBNAME=xbmc-pvr-addons
-SET VERSION=5f97406cffb412ac5161c3dc51205caca009fcc7
+SET VERSION=96774c4f775b156a46fb58151379dece3e773c96
 SET SOURCE=%LIBNAME%
 SET GIT_URL=git://github.com/opdenkamp/%LIBNAME%.git
 SET SOURCE_DIR=%TMP_DIR%\%SOURCE%
index 4271157..a6431eb 100644 (file)
@@ -94,6 +94,8 @@
       <backslash>ToggleFullScreen</backslash>
       <home>FirstPage</home>
       <end>LastPage</end>
+      <power>ActivateWindow(shutdownmenu)</power>
+      <sleep>ActivateWindow(shutdownmenu)</sleep>
       <!-- PVR windows -->
       <e>XBMC.ActivateWindowAndFocus(MyPVR, 31,0, 10,0)</e>
       <h>XBMC.ActivateWindowAndFocus(MyPVR, 32,0, 11,0)</h>
index 41914cb..83e25c2 100644 (file)
@@ -8,8 +8,7 @@
       <f4 mod="shift">ActivateWindow(music)</f4>    <!-- Green -->
       <f5 mod="shift">ActivateWindow(pictures)</f5> <!-- Yellow -->
       <f6 mod="shift">ActivateWindow(programs)</f6> <!-- Blue -->
-      <key id='61952'>ActivateWindow(shutdownmenu)</key> <!-- Power button -->
-      <key id='285'>ContextMenu</key>                    <!-- User button -->
+      <f4>ContextMenu</f4>                          <!-- User button -->
     </keyboard>
   </global>
 </keymap>
index 1ce8cb6..967b96c 100644 (file)
     <setting key="standby_pc_on_tv_standby" type="enum" value="13011" label="36029" order="7" lvalues="36028|13005|13011" />
     <setting key="standby_tv_on_pc_standby" type="bool" value="1" label="36026" order="8" />
     <setting key="use_tv_menu_language" type="bool" value="1" label="36018" order="9" />
+    <setting key="physical_address" type="string" label="36021" value="0" order="10" />
 
     <setting key="tv_vendor" type="int" value="0" configurable="0" />
     <setting key="device_name" type="string" value="XBMC" configurable="0" />
     <setting key="device_type" type="int" value="1" configurable="0" />
-    <setting key="physical_address" type="string" label="36021" value="0" configurable="0" />
     <setting key="cec_hdmi_port" type="int" value="1" label="36015" configurable="0" />
     <setting key="connected_device" type="int" label="36019" value="0" configurable="0" />
     <setting key="port" type="string" value="" label="36022" configurable="0" />
index 76027b6..a87dd5f 100644 (file)
@@ -13,6 +13,7 @@
     <rule name="rtv" protocols="rtv" player="DVDPlayer" />
     <rule name="hdhomerun/myth/mms/udp" protocols="hdhomerun|myth|cmyth|mms|mmsh|udp" player="DVDPlayer" />
     <rule name="lastfm/shout" protocols="lastfm|shout" player="PAPlayer" />
+    <rule name="rtmp" protocols="rtmp" player="videodefaultplayer" />
 
     <!-- dvdplayer can play standard rtsp streams -->
     <rule name="rtsp" protocols="rtsp" filetypes="!(rm|ra)"  player="PAPlayer" />
index 02a8ea2..2537929 100644 (file)
@@ -3,9 +3,13 @@ DEPS= ../Makefile.include Makefile prefix.patch
 
 # lib name, version
 LIBNAME=rtmpdump
-VERSION=2.3
+VERSION=e0056c51cc1710c9a44d2a2c4e2f344fa9cabcf4
+GIT_DIR=$(TARBALLS_LOCATION)/$(LIBNAME).git
+BASE_URL=git://git.ffmpeg.org/$(LIBNAME).git
 SOURCE=$(LIBNAME)-$(VERSION)
-ARCHIVE=$(SOURCE).tgz
+#tell git to use the addons repo rather than xbmc's repo
+export GIT_DIR
+export GIT_WORK_TREE=$(PLATFORM)
 
 # configuration settings
 
@@ -15,16 +19,25 @@ CLEAN_FILES=$(ARCHIVE) $(PLATFORM)
 
 all: .installed-$(PLATFORM)
 
-$(TARBALLS_LOCATION)/$(ARCHIVE):
-       $(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE)
+$(GIT_DIR)/HEAD:
+       cd $(TARBALLS_LOCATION); git clone --bare $(BASE_URL)
 
-$(PLATFORM): $(TARBALLS_LOCATION)/$(ARCHIVE) $(DEPS)
-       rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM)
-       $(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
+$(GIT_DIR)/current/$(VERSION): $(GIT_DIR)/HEAD $(DEPS)
+       git rev-list -1 $(VERSION) >/dev/null || git fetch origin "+refs/heads/*:refs/remotes/origin/*"
+       git rev-list -1 $(VERSION) >/dev/null
+       rm -rf $(GIT_DIR)/current; mkdir -p $(GIT_DIR)/current
+       touch $@
+
+$(PLATFORM)/bootstrap: $(GIT_DIR)/current/$(VERSION)
+       rm -rf $(PLATFORM); mkdir -p $(PLATFORM)
+       git checkout $(VERSION) -- .
+
+$(PLATFORM): $(PLATFORM)/bootstrap
+       cd $(PLATFORM); patch -p1 < ../librtmp-60-second-fix.patch
        cd $(PLATFORM); patch -p0 < ../prefix.patch
 
 $(LIBDYLIB): $(PLATFORM)
-       $(MAKE) -C $(PLATFORM)/librtmp CROSS_COMPILE=$(CROSSTOOLS) PREFIX=$(PREFIX)
+       $(MAKE) -C $(PLATFORM)/librtmp CROSS_COMPILE=$(CROSSTOOLS) PREFIX=$(PREFIX) XCFLAGS="$(CFLAGS)" XLDFLAGS="$(LDFLAGS) -lm"
 
 .installed-$(PLATFORM): $(LIBDYLIB)
        $(MAKE) -C $(PLATFORM)/librtmp install PREFIX=$(PREFIX)
diff --git a/tools/android/depends/librtmp/librtmp-60-second-fix.patch b/tools/android/depends/librtmp/librtmp-60-second-fix.patch
new file mode 100644 (file)
index 0000000..2914fc1
--- /dev/null
@@ -0,0 +1,1913 @@
+diff --git a/librtmp/amf.c b/librtmp/amf.c
+index ce84f81..a25bc04 100644
+--- a/librtmp/amf.c
++++ b/librtmp/amf.c
+@@ -610,6 +610,9 @@ AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
+       return -1;
+     }
++  if (*pBuffer == AMF_NULL)
++    bDecodeName = 0;
++
+   if (bDecodeName && nSize < 4)
+     {                         /* at least name (length + at least 1 byte) and 1 byte of data */
+       RTMP_Log(RTMP_LOGDEBUG,
+@@ -801,8 +804,8 @@ AMFProp_Dump(AMFObjectProperty *prop)
+     }
+   else
+     {
+-      name.av_val = "no-name.";
+-      name.av_len = sizeof("no-name.") - 1;
++      name.av_val = "no-name";
++      name.av_len = sizeof("no-name") - 1;
+     }
+   if (name.av_len > 18)
+     name.av_len = 18;
+diff --git a/librtmp/dh.h b/librtmp/dh.h
+index 9959532..e29587b 100644
+--- a/librtmp/dh.h
++++ b/librtmp/dh.h
+@@ -61,7 +61,7 @@ static int MDH_generate_key(MDH *dh)
+   MP_set(&dh->ctx.P, dh->p);
+   MP_set(&dh->ctx.G, dh->g);
+   dh->ctx.len = 128;
+-  dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs);
++  dhm_make_public(&dh->ctx, 1024, out, 1, havege_random, &RTMP_TLS_ctx->hs);
+   MP_new(dh->pub_key);
+   MP_new(dh->priv_key);
+   MP_set(dh->pub_key, &dh->ctx.GX);
+diff --git a/librtmp/handshake.h b/librtmp/handshake.h
+index 0438486..102ba82 100644
+--- a/librtmp/handshake.h
++++ b/librtmp/handshake.h
+@@ -965,8 +965,18 @@ HandShake(RTMP * r, int FP9HandShake)
+     __FUNCTION__);
+   RTMP_LogHex(RTMP_LOGDEBUG, reply, RTMP_SIG_SIZE);
+ #endif
+-  if (!WriteN(r, (char *)reply, RTMP_SIG_SIZE))
+-    return FALSE;
++  if (r->Link.CombineConnectPacket)
++    {
++      char *HandshakeResponse = malloc(RTMP_SIG_SIZE);
++      memcpy(HandshakeResponse, (char *) reply, RTMP_SIG_SIZE);
++      r->Link.HandshakeResponse.av_val = HandshakeResponse;
++      r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE;
++    }
++  else
++    {
++      if (!WriteN(r, (char *) reply, RTMP_SIG_SIZE))
++        return FALSE;
++    }
+   /* 2nd part of handshake */
+   if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
+diff --git a/librtmp/hashswf.c b/librtmp/hashswf.c
+index 9f4e2c0..eeed34c 100644
+--- a/librtmp/hashswf.c
++++ b/librtmp/hashswf.c
+@@ -70,7 +70,7 @@ extern TLS_CTX RTMP_TLS_ctx;
+ #endif /* CRYPTO */
+-#define       AGENT   "Mozilla/5.0"
++#define       AGENT   "Mozilla/5.0 (Windows NT 5.1; rv:8.0) Gecko/20100101 Firefox/8.0"
+ HTTPResult
+ HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
+@@ -528,7 +528,7 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
+         if (strncmp(buf, "url: ", 5))
+           continue;
+-        if (strncmp(buf + 5, url, hlen))
++        if (strncmp(buf + 5, url, strlen(buf + 5) - 1))
+           continue;
+         r1 = strrchr(buf, '/');
+         i = strlen(r1);
+diff --git a/librtmp/log.c b/librtmp/log.c
+index 0012985..856e3e4 100644
+--- a/librtmp/log.c
++++ b/librtmp/log.c
+@@ -52,8 +52,8 @@ static void rtmp_log_default(int level, const char *format, va_list vl)
+       vsnprintf(str, MAX_PRINT_LEN-1, format, vl);
+       /* Filter out 'no-name' */
+-      if ( RTMP_debuglevel<RTMP_LOGALL && strstr(str, "no-name" ) != NULL )
+-              return;
++      if (RTMP_debuglevel < RTMP_LOGDEBUG && strstr(str, "no-name") != NULL)
++        return;
+       if ( !fmsg ) fmsg = stderr;
+diff --git a/librtmp/rtmp.c b/librtmp/rtmp.c
+index 52d0254..bef37aa 100644
+--- a/librtmp/rtmp.c
++++ b/librtmp/rtmp.c
+@@ -27,6 +27,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <assert.h>
++#include <math.h>
+ #include "rtmp_sys.h"
+ #include "log.h"
+@@ -45,6 +46,7 @@ TLS_CTX RTMP_TLS_ctx;
+ #define RTMP_SIG_SIZE 1536
+ #define RTMP_LARGE_HEADER_SIZE 12
++#define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
+ static const int packetSize[] = { 12, 8, 4, 1 };
+@@ -97,6 +99,9 @@ static int SendFCSubscribe(RTMP *r, AVal *subscribepath);
+ static int SendPlay(RTMP *r);
+ static int SendBytesReceived(RTMP *r);
+ static int SendUsherToken(RTMP *r, AVal *usherToken);
++static int SendInvoke(RTMP *r, AVal *Command, int queue);
++static int SendGetStreamLength(RTMP *r);
++static int strsplit(char *src, int srclen, char delim, char ***params);
+ #if 0                         /* unused */
+ static int SendBGHasStream(RTMP *r, double dId, AVal *playpath);
+@@ -259,6 +264,8 @@ RTMP_Init(RTMP *r)
+   r->m_fVideoCodecs = 252.0;
+   r->Link.timeout = 30;
+   r->Link.swfAge = 30;
++  r->Link.CombineConnectPacket = TRUE;
++  r->Link.ConnectPacket = FALSE;
+ }
+ void
+@@ -337,6 +344,7 @@ RTMP_SetupStream(RTMP *r,
+                AVal *flashVer,
+                AVal *subscribepath,
+                AVal *usherToken,
++               AVal *WeebToken,
+                int dStart,
+                int dStop, int bLiveStream, long int timeout)
+ {
+@@ -359,6 +367,8 @@ RTMP_SetupStream(RTMP *r,
+     RTMP_Log(RTMP_LOGDEBUG, "subscribepath : %s", subscribepath->av_val);
+   if (usherToken && usherToken->av_val)
+     RTMP_Log(RTMP_LOGDEBUG, "NetStream.Authenticate.UsherToken : %s", usherToken->av_val);
++  if (WeebToken && WeebToken->av_val)
++    RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", WeebToken->av_val);
+   if (flashVer && flashVer->av_val)
+     RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val);
+   if (dStart > 0)
+@@ -426,6 +436,8 @@ RTMP_SetupStream(RTMP *r,
+     r->Link.subscribepath = *subscribepath;
+   if (usherToken && usherToken->av_len)
+     r->Link.usherToken = *usherToken;
++  if (WeebToken && WeebToken->av_len)
++    r->Link.WeebToken = *WeebToken;
+   r->Link.seekTime = dStart;
+   r->Link.stopTime = dStop;
+   if (bLiveStream)
+@@ -483,14 +495,22 @@ static struct urlopt {
+       "Stream is live, no seeking possible" },
+   { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, 0,
+       "Stream to subscribe to" },
+-  { AVC("jtv"), OFF(Link.usherToken),          OPT_STR, 0,
+-      "Justin.tv authentication token" },
+-  { AVC("token"),     OFF(Link.token),               OPT_STR, 0,
++  { AVC("jtv"),       OFF(Link.usherToken),    OPT_STR, 0,
++        "Justin.tv authentication token"},
++  { AVC("weeb"),      OFF(Link.WeebToken),     OPT_STR, 0,
++        "Weeb.tv authentication token"},
++  { AVC("token"),     OFF(Link.token),         OPT_STR, 0,
+       "Key for SecureToken response" },
+   { AVC("swfVfy"),    OFF(Link.lFlags),        OPT_BOOL, RTMP_LF_SWFV,
+       "Perform SWF Verification" },
+   { AVC("swfAge"),    OFF(Link.swfAge),        OPT_INT, 0,
+       "Number of days to use cached SWF hash" },
++#ifdef CRYPTO
++  { AVC("swfsize"),   OFF(Link.swfSize),       OPT_INT, 0,
++        "Size of the decompressed SWF file"},
++  { AVC("swfhash"),   OFF(Link.swfHash),       OPT_STR, 0,
++        "SHA256 hash of the decompressed SWF file"},
++#endif
+   { AVC("start"),     OFF(Link.seekTime),      OPT_INT, 0,
+       "Stream start position in milliseconds" },
+   { AVC("stop"),      OFF(Link.stopTime),      OPT_INT, 0,
+@@ -751,9 +771,16 @@ int RTMP_SetupURL(RTMP *r, char *url)
+     }
+ #ifdef CRYPTO
+-  if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
+-    RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize,
+-        (unsigned char *)r->Link.SWFHash, r->Link.swfAge);
++  RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %d %d %s\n", r->Link.swfSize, r->Link.swfHash.av_len, r->Link.swfHash.av_val);
++  if (r->Link.swfSize && r->Link.swfHash.av_len)
++    {
++      int i, j = 0;
++      for (i = 0; i < r->Link.swfHash.av_len; i += 2)
++        r->Link.SWFHash[j++] = (HEX2BIN(r->Link.swfHash.av_val[i]) << 4) | HEX2BIN(r->Link.swfHash.av_val[i + 1]);
++      r->Link.SWFSize = (uint32_t) r->Link.swfSize;
++    }
++  else if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
++    RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize, (unsigned char *) r->Link.SWFHash, r->Link.swfAge);
+ #endif
+   if (r->Link.port == 0)
+@@ -854,6 +881,8 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service)
+   }
+   setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on));
++  if (r->Link.protocol & RTMP_FEATURE_HTTP)
++    setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on));
+   return TRUE;
+ }
+@@ -1308,8 +1337,24 @@ ReadN(RTMP *r, char *buffer, int n)
+                 return 0;
+               }
+           }
+-        if (r->m_resplen && !r->m_sb.sb_size)
+-          RTMPSockBuf_Fill(&r->m_sb);
++
++          // Try to fill the whole buffer. previous buffer needs to be consumed
++          // completely before receiving new data.
++          if (r->m_resplen && (r->m_sb.sb_size <= 0))
++            {
++              do
++                {
++                  nBytes = RTMPSockBuf_Fill(&r->m_sb);
++                  if (nBytes == -1)
++                    {
++                      if (!r->m_sb.sb_timedout)
++                        RTMP_Close(r);
++                      return 0;
++                    }
++                }
++              while (r->m_resplen && (r->m_sb.sb_size < r->m_resplen) && (nBytes > 0));
++            }
++
+           avail = r->m_sb.sb_size;
+         if (avail > r->m_resplen)
+           avail = r->m_resplen;
+@@ -1336,10 +1381,9 @@ ReadN(RTMP *r, char *buffer, int n)
+         r->m_sb.sb_size -= nRead;
+         nBytes = nRead;
+         r->m_nBytesIn += nRead;
+-        if (r->m_bSendCounter
+-            && r->m_nBytesIn > ( r->m_nBytesInSent + r->m_nClientBW / 10))
+-          if (!SendBytesReceived(r))
+-              return FALSE;
++          if (r->m_bSendCounter && r->m_nBytesIn > (r->m_nBytesInSent + r->m_nClientBW / 10))
++            if (!SendBytesReceived(r))
++              return FALSE;
+       }
+       /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
+ #ifdef _DEBUG
+@@ -1390,6 +1434,16 @@ WriteN(RTMP *r, const char *buffer, int n)
+     }
+ #endif
++  if (r->Link.ConnectPacket)
++    {
++      char *ConnectPacket = malloc(r->Link.HandshakeResponse.av_len + n);
++      memcpy(ConnectPacket, r->Link.HandshakeResponse.av_val, r->Link.HandshakeResponse.av_len);
++      memcpy(ConnectPacket + r->Link.HandshakeResponse.av_len, ptr, n);
++      ptr = ConnectPacket;
++      n += r->Link.HandshakeResponse.av_len;
++      r->Link.ConnectPacket = FALSE;
++    }
++
+   while (n > 0)
+     {
+       int nBytes;
+@@ -1455,6 +1509,9 @@ SendConnectPacket(RTMP *r, RTMPPacket *cp)
+   char pbuf[4096], *pend = pbuf + sizeof(pbuf);
+   char *enc;
++  if (r->Link.CombineConnectPacket)
++    r->Link.ConnectPacket = TRUE;
++
+   if (cp)
+     return RTMP_SendPacket(r, cp, TRUE);
+@@ -1667,7 +1724,7 @@ SendUsherToken(RTMP *r, AVal *usherToken)
+   packet.m_hasAbsTimestamp = 0;
+   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
+-  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %s", usherToken->av_val);
++  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %.*s", usherToken->av_len, usherToken->av_val);
+   enc = packet.m_body;
+   enc = AMF_EncodeString(enc, pend, &av_NetStream_Authenticate_UsherToken);
+   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+@@ -2096,10 +2153,8 @@ SendPlay(RTMP *r)
+     enc = AMF_EncodeNumber(enc, pend, -1000.0);
+   else
+     {
+-      if (r->Link.seekTime > 0.0)
+-      enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime);    /* resume from here */
+-      else
+-      enc = AMF_EncodeNumber(enc, pend, 0.0); /*-2000.0);*/ /* recorded as default, -2000.0 is not reliable since that freezes the player if the stream is not found */
++      if (r->Link.seekTime > 0.0 || r->Link.stopTime)
++        enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime); /* resume from here */
+     }
+   if (!enc)
+     return FALSE;
+@@ -2215,7 +2270,7 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
+   int nSize;
+   char *buf;
+-  RTMP_Log(RTMP_LOGDEBUG, "sending ctrl. type: 0x%04x", (unsigned short)nType);
++  RTMP_Log(RTMP_LOGDEBUG, "sending ctrl, type: 0x%04x", (unsigned short)nType);
+   packet.m_nChannel = 0x02;   /* control channel (ping) */
+   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+@@ -2247,8 +2302,8 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
+     }
+   else if (nType == 0x1A)
+     {
+-        *buf = nObject & 0xff;
+-      }
++      *buf = nObject & 0xff;
++    }
+   else
+     {
+       if (nSize > 2)
+@@ -2305,6 +2360,7 @@ AV_clear(RTMP_METHOD *vals, int num)
+   free(vals);
+ }
++SAVC(onBWCheck);
+ SAVC(onBWDone);
+ SAVC(onFCSubscribe);
+ SAVC(onFCUnsubscribe);
+@@ -2314,24 +2370,26 @@ SAVC(_error);
+ SAVC(close);
+ SAVC(code);
+ SAVC(level);
++SAVC(description);
+ SAVC(onStatus);
+ SAVC(playlist_ready);
+ static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
+ static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed");
+-static const AVal av_NetStream_Play_StreamNotFound =
+-AVC("NetStream.Play.StreamNotFound");
+-static const AVal av_NetConnection_Connect_InvalidApp =
+-AVC("NetConnection.Connect.InvalidApp");
++static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound");
++static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp");
+ static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
+ static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete");
+ static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
+ static const AVal av_NetStream_Seek_Notify = AVC("NetStream.Seek.Notify");
+ static const AVal av_NetStream_Pause_Notify = AVC("NetStream.Pause.Notify");
+-static const AVal av_NetStream_Play_PublishNotify =
+-AVC("NetStream.Play.PublishNotify");
+-static const AVal av_NetStream_Play_UnpublishNotify =
+-AVC("NetStream.Play.UnpublishNotify");
++static const AVal av_NetStream_Play_PublishNotify = AVC("NetStream.Play.PublishNotify");
++static const AVal av_NetStream_Play_UnpublishNotify = AVC("NetStream.Play.UnpublishNotify");
+ static const AVal av_NetStream_Publish_Start = AVC("NetStream.Publish.Start");
++static const AVal av_NetConnection_confStream = AVC("NetConnection.confStream");
++static const AVal av_verifyClient = AVC("verifyClient");
++static const AVal av_sendStatus = AVC("sendStatus");
++static const AVal av_getStreamLength = AVC("getStreamLength");
++static const AVal av_ReceiveCheckPublicStatus = AVC("ReceiveCheckPublicStatus");
+ /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
+ static int
+@@ -2341,6 +2399,11 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
+   AVal method;
+   double txn;
+   int ret = 0, nRes;
++  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc, **params = NULL;
++  char *host = r->Link.hostname.av_len ? r->Link.hostname.av_val : "";
++  char *pageUrl = r->Link.pageUrl.av_len ? r->Link.pageUrl.av_val : "";
++  int param_count;
++  AVal av_Command, av_Response;
+   if (body[0] != 0x02)                /* make sure it is a string method name we start with */
+     {
+       RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
+@@ -2402,23 +2465,137 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
+             RTMP_SendServerBW(r);
+             RTMP_SendCtrl(r, 3, 0, 300);
+           }
+-        RTMP_SendCreateStream(r);
+-
+-        if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
+-          {
+-            /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
+-            if (r->Link.usherToken.av_len)
+-              SendUsherToken(r, &r->Link.usherToken);
+-            /* Send the FCSubscribe if live stream or if subscribepath is set */
+-            if (r->Link.subscribepath.av_len)
+-              SendFCSubscribe(r, &r->Link.subscribepath);
+-            else if (r->Link.lFlags & RTMP_LF_LIVE)
+-              SendFCSubscribe(r, &r->Link.playpath);
+-          }
+-      }
++          if (strstr(host, "tv-stream.to") || strstr(pageUrl, "tv-stream.to"))
++            {
++              static char auth[] = {'h', 0xC2, 0xA7, '4', 'j', 'h', 'H', '4', '3', 'd'};
++              AVal av_auth;
++              SAVC(requestAccess);
++              av_auth.av_val = auth;
++              av_auth.av_len = sizeof (auth);
++
++              enc = pbuf;
++              enc = AMF_EncodeString(enc, pend, &av_requestAccess);
++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
++              *enc++ = AMF_NULL;
++              enc = AMF_EncodeString(enc, pend, &av_auth);
++              av_Command.av_val = pbuf;
++              av_Command.av_len = enc - pbuf;
++              SendInvoke(r, &av_Command, FALSE);
++
++              SAVC(getConnectionCount);
++              enc = pbuf;
++              enc = AMF_EncodeString(enc, pend, &av_getConnectionCount);
++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
++              *enc++ = AMF_NULL;
++              av_Command.av_val = pbuf;
++              av_Command.av_len = enc - pbuf;
++              SendInvoke(r, &av_Command, FALSE);
++
++              SendGetStreamLength(r);
++            }
++          else if (strstr(host, "jampo.com.ua") || strstr(pageUrl, "jampo.com.ua"))
++            {
++              SendGetStreamLength(r);
++            }
++          else if (strstr(host, "streamscene.cc") || strstr(pageUrl, "streamscene.cc")
++                   || strstr(host, "tsboard.tv") || strstr(pageUrl, "teamstream.in"))
++            {
++              SAVC(r);
++              enc = pbuf;
++              enc = AMF_EncodeString(enc, pend, &av_r);
++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
++              *enc++ = AMF_NULL;
++              av_Command.av_val = pbuf;
++              av_Command.av_len = enc - pbuf;
++              SendInvoke(r, &av_Command, FALSE);
++
++              SendGetStreamLength(r);
++            }
++          else if (strstr(host, "chaturbate.com") || strstr(pageUrl, "chaturbate.com"))
++            {
++              AVal av_ModelName;
++              SAVC(CheckPublicStatus);
++
++              if (strlen(pageUrl) > 7)
++                {
++                  strsplit(pageUrl + 7, FALSE, '/', &params);
++                  av_ModelName.av_val = params[1];
++                  av_ModelName.av_len = strlen(params[1]);
++
++                  enc = pbuf;
++                  enc = AMF_EncodeString(enc, pend, &av_CheckPublicStatus);
++                  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
++                  *enc++ = AMF_NULL;
++                  enc = AMF_EncodeString(enc, pend, &av_ModelName);
++                  av_Command.av_val = pbuf;
++                  av_Command.av_len = enc - pbuf;
++
++                  SendInvoke(r, &av_Command, FALSE);
++                }
++              else
++                {
++                  RTMP_Log(RTMP_LOGERROR, "you must specify the pageUrl");
++                  RTMP_Close(r);
++                }
++            }
++          /* Weeb.tv specific authentication */
++          else if (r->Link.WeebToken.av_len)
++            {
++              AVal av_Token, av_Username, av_Password;
++              SAVC(determineAccess);
++
++              param_count = strsplit(r->Link.WeebToken.av_val, FALSE, ';', &params);
++              if (param_count >= 1)
++                {
++                  av_Token.av_val = params[0];
++                  av_Token.av_len = strlen(params[0]);
++                }
++              if (param_count >= 2)
++                {
++                  av_Username.av_val = params[1];
++                  av_Username.av_len = strlen(params[1]);
++                }
++              if (param_count >= 3)
++                {
++                  av_Password.av_val = params[2];
++                  av_Password.av_len = strlen(params[2]);
++                }
++
++              enc = pbuf;
++              enc = AMF_EncodeString(enc, pend, &av_determineAccess);
++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
++              *enc++ = AMF_NULL;
++              enc = AMF_EncodeString(enc, pend, &av_Token);
++              enc = AMF_EncodeString(enc, pend, &av_Username);
++              enc = AMF_EncodeString(enc, pend, &av_Password);
++              av_Command.av_val = pbuf;
++              av_Command.av_len = enc - pbuf;
++
++              RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", r->Link.WeebToken.av_val);
++              SendInvoke(r, &av_Command, FALSE);
++            }
++          else
++            RTMP_SendCreateStream(r);
++        }
++      else if (AVMATCH(&methodInvoked, &av_getStreamLength))
++        {
++          RTMP_SendCreateStream(r);
++        }
+       else if (AVMATCH(&methodInvoked, &av_createStream))
+-      {
+-        r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
++        {
++          r->m_stream_id = (int) AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
++
++          if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
++            {
++              /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
++              if (r->Link.usherToken.av_len)
++                SendUsherToken(r, &r->Link.usherToken);
++              /* Send the FCSubscribe if live stream or if subscribepath is set */
++              if (r->Link.subscribepath.av_len)
++                SendFCSubscribe(r, &r->Link.subscribepath);
++              else if ((r->Link.lFlags & RTMP_LF_LIVE) && (!r->Link.WeebToken.av_len))
++                SendFCSubscribe(r, &r->Link.playpath);
++            }
+         if (r->Link.protocol & RTMP_FEATURE_WRITE)
+           {
+@@ -2441,7 +2618,7 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
+     }
+   else if (AVMATCH(&method, &av_onBWDone))
+     {
+-        if (!r->m_nBWCheckCounter)
++      if (!r->m_nBWCheckCounter)
+         SendCheckBW(r);
+     }
+   else if (AVMATCH(&method, &av_onFCSubscribe))
+@@ -2457,7 +2634,7 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
+     {
+       SendPong(r, txn);
+     }
+-  else if (AVMATCH(&method, &av__onbwcheck))
++  else if (AVMATCH(&method, &av__onbwcheck) || AVMATCH(&method, &av_onBWCheck))
+     {
+       SendCheckBWResult(r, txn);
+     }
+@@ -2473,20 +2650,63 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
+     }
+   else if (AVMATCH(&method, &av__error))
+     {
+-      RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
++      double code = 0;
++      unsigned int parsedPort;
++      AMFObject obj2;
++      AMFObjectProperty p;
++      AVal redirect;
++      SAVC(ex);
++      SAVC(redirect);
++
++      AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
++      if (RTMP_FindFirstMatchingProperty(&obj2, &av_ex, &p))
++        {
++          AMFProp_GetObject(&p, &obj2);
++          if (RTMP_FindFirstMatchingProperty(&obj2, &av_code, &p))
++            code = AMFProp_GetNumber(&p);
++          if (code == 302 && RTMP_FindFirstMatchingProperty(&obj2, &av_redirect, &p))
++            {
++              AMFProp_GetString(&p, &redirect);
++              r->Link.redirected = TRUE;
++
++              char *url = malloc(redirect.av_len + sizeof ("/playpath"));
++              strncpy(url, redirect.av_val, redirect.av_len);
++              url[redirect.av_len] = '\0';
++              r->Link.tcUrl.av_val = url;
++              r->Link.tcUrl.av_len = redirect.av_len;
++              strcat(url, "/playpath");
++              RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname, &parsedPort, &r->Link.playpath0, &r->Link.app);
++              r->Link.port = parsedPort;
++            }
++        }
++      if (r->Link.redirected)
++        RTMP_Log(RTMP_LOGINFO, "rtmp server sent redirect");
++      else
++        RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
+     }
+   else if (AVMATCH(&method, &av_close))
+     {
+-      RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
+-      RTMP_Close(r);
++      if (r->Link.redirected)
++        {
++          RTMP_Log(RTMP_LOGINFO, "trying to connect with redirected url");
++          RTMP_Close(r);
++          r->Link.redirected = FALSE;
++          RTMP_Connect(r, NULL);
++        }
++      else
++        {
++          RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
++          RTMP_Close(r);
++        }
+     }
+   else if (AVMATCH(&method, &av_onStatus))
+     {
+       AMFObject obj2;
+-      AVal code, level;
++      AVal code, level, description;
+       AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
+       AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code);
+       AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level);
++      AMFProp_GetString(AMF_GetProp(&obj2, &av_description, -1), &description);
+       RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val);
+       if (AVMATCH(&code, &av_NetStream_Failed)
+@@ -2550,6 +2770,45 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
+           r->m_pausing = 3;
+         }
+       }
++
++      else if (AVMATCH(&code, &av_NetConnection_confStream))
++        {
++#ifdef CRYPTO
++          static const char hexdig[] = "0123456789abcdef";
++          SAVC(cf_stream);
++          int i;
++          char hash_hex[33] = {0};
++          unsigned char hash[16];
++          AVal auth;
++          param_count = strsplit(description.av_val, description.av_len, ':', &params);
++          if (param_count >= 3)
++            {
++              char *buf = malloc(strlen(params[0]) + r->Link.playpath.av_len + 1);
++              strcpy(buf, params[0]);
++              strncat(buf, r->Link.playpath.av_val, r->Link.playpath.av_len);
++              md5_hash((unsigned char *) buf, strlen(buf), hash);
++              for (i = 0; i < 16; i++)
++                {
++                  hash_hex[i * 2] = hexdig[0x0f & (hash[i] >> 4)];
++                  hash_hex[i * 2 + 1] = hexdig[0x0f & (hash[i])];
++                }
++              auth.av_val = &hash_hex[atoi(params[1]) - 1];
++              auth.av_len = atoi(params[2]);
++              RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %.*s", auth.av_len, auth.av_val);
++
++              enc = pbuf;
++              enc = AMF_EncodeString(enc, pend, &av_cf_stream);
++              enc = AMF_EncodeNumber(enc, pend, txn);
++              *enc++ = AMF_NULL;
++              enc = AMF_EncodeString(enc, pend, &auth);
++              av_Command.av_val = pbuf;
++              av_Command.av_len = enc - pbuf;
++
++              SendInvoke(r, &av_Command, FALSE);
++              free(buf);
++            }
++#endif
++        }
+     }
+   else if (AVMATCH(&method, &av_playlist_ready))
+     {
+@@ -2563,6 +2822,74 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
+           }
+         }
+     }
++  else if (AVMATCH(&method, &av_verifyClient))
++    {
++      double VerificationNumber = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
++      RTMP_Log(RTMP_LOGDEBUG, "VerificationNumber: %.2f", VerificationNumber);
++
++      enc = pbuf;
++      enc = AMF_EncodeString(enc, pend, &av__result);
++      enc = AMF_EncodeNumber(enc, pend, txn);
++      *enc++ = AMF_NULL;
++      enc = AMF_EncodeNumber(enc, pend, exp(atan(sqrt(VerificationNumber))) + 1);
++      av_Response.av_val = pbuf;
++      av_Response.av_len = enc - pbuf;
++
++      AMF_Decode(&obj, av_Response.av_val, av_Response.av_len, FALSE);
++      AMF_Dump(&obj);
++      SendInvoke(r, &av_Response, FALSE);
++    }
++  else if (AVMATCH(&method, &av_sendStatus))
++    {
++      if (r->Link.WeebToken.av_len)
++        {
++          AVal av_Authorized = AVC("User.hasAccess");
++          AVal av_TransferLimit = AVC("User.noPremium.limited");
++          AVal av_UserLimit = AVC("User.noPremium.tooManyUsers");
++          AVal av_TimeLeft = AVC("timeLeft");
++          AVal av_Status, av_ReconnectionTime;
++
++          AMFObject Status;
++          AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &Status);
++          AMFProp_GetString(AMF_GetProp(&Status, &av_code, -1), &av_Status);
++          RTMP_Log(RTMP_LOGINFO, "%.*s", av_Status.av_len, av_Status.av_val);
++          if (AVMATCH(&av_Status, &av_Authorized))
++            {
++              RTMP_Log(RTMP_LOGINFO, "Weeb.tv authentication successful");
++              RTMP_SendCreateStream(r);
++            }
++          else if (AVMATCH(&av_Status, &av_UserLimit))
++            {
++              RTMP_Log(RTMP_LOGINFO, "No free slots available");
++              RTMP_Close(r);
++            }
++          else if (AVMATCH(&av_Status, &av_TransferLimit))
++            {
++              AMFProp_GetString(AMF_GetProp(&Status, &av_TimeLeft, -1), &av_ReconnectionTime);
++              RTMP_Log(RTMP_LOGINFO, "Viewing limit exceeded. try again in %.*s minutes.", av_ReconnectionTime.av_len, av_ReconnectionTime.av_val);
++              RTMP_Close(r);
++            }
++        }
++    }
++  else if (AVMATCH(&method, &av_ReceiveCheckPublicStatus))
++    {
++      AVal Status;
++      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &Status);
++      strsplit(Status.av_val, Status.av_len, ',', &params);
++      if (strcmp(params[0], "0") == 0)
++        {
++          RTMP_Log(RTMP_LOGINFO, "Model status is %s", params[1]);
++          RTMP_Close(r);
++        }
++      else
++        {
++          AVal Playpath;
++          Playpath.av_val = params[1];
++          Playpath.av_len = strlen(params[1]);
++          RTMP_ParsePlaypath(&Playpath, &r->Link.playpath);
++          RTMP_SendCreateStream(r);
++        }
++    }
+   else
+     {
+@@ -2748,7 +3075,7 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet)
+   unsigned int tmp;
+   if (packet->m_body && packet->m_nBodySize >= 2)
+     nType = AMF_DecodeInt16(packet->m_body);
+-  RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType,
++  RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl, type: %d, len: %d", __FUNCTION__, nType,
+       packet->m_nBodySize);
+   /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
+@@ -2856,15 +3183,15 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet)
+       RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__);
+       if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01)
+       {
+-        RTMP_Log(RTMP_LOGERROR,
+-            "%s: SWFVerification Type %d request not supported! Patches welcome...",
+-          __FUNCTION__, packet->m_body[2]);
++          RTMP_Log(RTMP_LOGERROR,
++                   "%s: SWFVerification Type %d request not supported, attempting to use SWFVerification Type 1! Patches welcome...",
++                   __FUNCTION__, packet->m_body[2]);
+       }
+ #ifdef CRYPTO
+       /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
+       /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
+-      else if (r->Link.SWFSize)
++      if (r->Link.SWFSize)
+       {
+         RTMP_SendCtrl(r, 0x1B, 0, 0);
+       }
+@@ -3142,8 +3469,18 @@ HandShake(RTMP *r, int FP9HandShake)
+       serversig[4], serversig[5], serversig[6], serversig[7]);
+   /* 2nd part of handshake */
+-  if (!WriteN(r, serversig, RTMP_SIG_SIZE))
+-    return FALSE;
++  if (r->Link.CombineConnectPacket)
++    {
++      char *HandshakeResponse = malloc(RTMP_SIG_SIZE);
++      memcpy(HandshakeResponse, (char *) serversig, RTMP_SIG_SIZE);
++      r->Link.HandshakeResponse.av_val = HandshakeResponse;
++      r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE;
++    }
++  else
++    {
++      if (!WriteN(r, (char *) serversig, RTMP_SIG_SIZE))
++        return FALSE;
++    }
+   if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
+     return FALSE;
+@@ -3709,12 +4046,11 @@ HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len)
+   char hbuf[512];
+   int hlen = snprintf(hbuf, sizeof(hbuf), "POST /%s%s/%d HTTP/1.1\r\n"
+     "Host: %.*s:%d\r\n"
+-    "Accept: */*\r\n"
+-    "User-Agent: Shockwave Flash\n"
+-    "Connection: Keep-Alive\n"
++    "User-Agent: Shockwave Flash\r\n"
++    "Connection: Keep-Alive\r\n"
+     "Cache-Control: no-cache\r\n"
+-    "Content-type: application/x-fcs\r\n"
+-    "Content-length: %d\r\n\r\n", RTMPT_cmds[cmd],
++    "Content-Type: application/x-fcs\r\n"
++    "Content-Length: %d\r\n\r\n", RTMPT_cmds[cmd],
+     r->m_clientID.av_val ? r->m_clientID.av_val : "",
+     r->m_msgCounter, r->Link.hostname.av_len, r->Link.hostname.av_val,
+     r->Link.port, len);
+@@ -3749,6 +4085,14 @@ HTTP_read(RTMP *r, int fill)
+   if (!ptr)
+     return -1;
+   ptr += 4;
++  int resplen = r->m_sb.sb_size - (ptr - r->m_sb.sb_start);
++  if (hlen < 4096)
++    while (resplen < hlen)
++      {
++        if (RTMPSockBuf_Fill(&r->m_sb) == -1)
++          return -1;
++        resplen = r->m_sb.sb_size - (ptr - r->m_sb.sb_start);
++      }
+   r->m_sb.sb_size -= ptr - r->m_sb.sb_start;
+   r->m_sb.sb_start = ptr;
+   r->m_unackd--;
+@@ -4301,13 +4645,21 @@ fail:
+                 r->m_read.status = nRead;
+                 goto fail;
+               }
+-            /* buffer overflow, fix buffer and give up */
+-            if (r->m_read.buf < mybuf || r->m_read.buf > end) {
+-              mybuf = realloc(mybuf, cnt + nRead);
+-              memcpy(mybuf+cnt, r->m_read.buf, nRead);
+-              r->m_read.buf = mybuf+cnt+nRead;
+-              break;
+-            }
++              /* buffer overflow, fix buffer and give up */
++              if (r->m_read.buf < mybuf || r->m_read.buf > end)
++                {
++                  if (!cnt)
++                    {
++                      mybuf = realloc(mybuf, sizeof (flvHeader) + cnt + nRead);
++                      memcpy(mybuf, flvHeader, sizeof (flvHeader));
++                      cnt += sizeof (flvHeader);
++                    }
++                  else
++                    mybuf = realloc(mybuf, cnt + nRead);
++                  memcpy(mybuf + cnt, r->m_read.buf, nRead);
++                  r->m_read.buf = mybuf + cnt + nRead;
++                  break;
++                }
+             cnt += nRead;
+             r->m_read.buf += nRead;
+             r->m_read.buflen -= nRead;
+@@ -4458,3 +4810,90 @@ RTMP_Write(RTMP *r, const char *buf, int size)
+     }
+   return size+s2;
+ }
++
++static int
++SendInvoke(RTMP *r, AVal *Command, int queue)
++{
++  RTMPPacket packet;
++  char pbuf[512], *enc;
++
++  packet.m_nChannel = 0x03; /* control channel (invoke) */
++  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
++  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
++  packet.m_nTimeStamp = 0;
++  packet.m_nInfoField2 = 0;
++  packet.m_hasAbsTimestamp = 0;
++  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
++
++  enc = packet.m_body;
++  if (Command->av_len)
++    {
++      memcpy(enc, Command->av_val, Command->av_len);
++      enc += Command->av_len;
++    }
++  else
++    return FALSE;
++  packet.m_nBodySize = enc - packet.m_body;
++
++  return RTMP_SendPacket(r, &packet, queue);
++}
++
++static int
++strsplit(char *src, int srclen, char delim, char ***params)
++{
++  char *sptr, *srcbeg, *srcend, *dstr;
++  int count = 1, i = 0, len = 0;
++
++  if (src == NULL)
++    return 0;
++  if (!srclen)
++    srclen = strlen(src);
++  srcbeg = src;
++  srcend = srcbeg + srclen;
++  sptr = srcbeg;
++
++  /* count the delimiters */
++  while (sptr < srcend)
++    {
++      if (*sptr++ == delim)
++        count++;
++    }
++  sptr = srcbeg;
++  *params = calloc(count, sizeof (size_t));
++  char **param = *params;
++
++  for (i = 0; i < (count - 1); i++)
++    {
++      dstr = strchr(sptr, delim);
++      len = dstr - sptr;
++      param[i] = calloc(len + 1, sizeof (char));
++      strncpy(param[i], sptr, len);
++      sptr += len + 1;
++    }
++
++  /* copy the last string */
++  if (sptr <= srcend)
++    {
++      len = srclen - (sptr - srcbeg);
++      param[i] = calloc(len + 1, sizeof (char));
++      strncpy(param[i], sptr, len);
++    }
++  return count;
++}
++
++static int
++SendGetStreamLength(RTMP *r)
++{
++  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc;
++  AVal av_Command;
++
++  enc = pbuf;
++  enc = AMF_EncodeString(enc, pend, &av_getStreamLength);
++  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
++  *enc++ = AMF_NULL;
++  enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
++  av_Command.av_val = pbuf;
++  av_Command.av_len = enc - pbuf;
++
++  return SendInvoke(r, &av_Command, TRUE);
++}
+diff --git a/librtmp/rtmp.h b/librtmp/rtmp.h
+index 6b2ae5b..411b488 100644
+--- a/librtmp/rtmp.h
++++ b/librtmp/rtmp.h
+@@ -150,12 +150,14 @@ extern "C"
+     AVal playpath;    /* passed in explicitly */
+     AVal tcUrl;
+     AVal swfUrl;
++    AVal swfHash;
+     AVal pageUrl;
+     AVal app;
+     AVal auth;
+     AVal flashVer;
+     AVal subscribepath;
+     AVal usherToken;
++    AVal WeebToken;
+     AVal token;
+     AMFObject extras;
+     int edepth;
+@@ -172,9 +174,15 @@ extern "C"
+     int lFlags;
+     int swfAge;
++    int swfSize;
+     int protocol;
++    int ConnectPacket;
++    int CombineConnectPacket;
++    int redirected;
+     int timeout;              /* connection timeout in seconds */
++    AVal Extras;
++    AVal HandshakeResponse;
+     unsigned short socksport;
+     unsigned short port;
+@@ -299,6 +307,7 @@ extern "C"
+                       AVal *flashVer,
+                       AVal *subscribepath,
+                       AVal *usherToken,
++                      AVal *WeebToken,
+                       int dStart,
+                       int dStop, int bLiveStream, long int timeout);
+diff --git a/librtmp/rtmp_sys.h b/librtmp/rtmp_sys.h
+index c3fd4a6..1bfb562 100644
+--- a/librtmp/rtmp_sys.h
++++ b/librtmp/rtmp_sys.h
+@@ -64,6 +64,7 @@
+ #include <polarssl/net.h>
+ #include <polarssl/ssl.h>
+ #include <polarssl/havege.h>
++#include <polarssl/md5.h>
+ typedef struct tls_ctx {
+       havege_state hs;
+       ssl_session ssn;
+@@ -71,7 +72,7 @@ typedef struct tls_ctx {
+ #define TLS_CTX tls_ctx *
+ #define TLS_client(ctx,s)     s = malloc(sizeof(ssl_context)); ssl_init(s);\
+       ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\
+-      ssl_set_rng(s, havege_rand, &ctx->hs);\
++      ssl_set_rng(s, havege_random, &ctx->hs);\
+       ssl_set_ciphersuites(s, ssl_default_ciphersuites);\
+       ssl_set_session(s, 1, 600, &ctx->ssn)
+ #define TLS_setfd(s,fd)       ssl_set_bio(s, net_recv, &fd, net_send, &fd)
+@@ -80,6 +81,7 @@ typedef struct tls_ctx {
+ #define TLS_write(s,b,l)      ssl_write(s,(unsigned char *)b,l)
+ #define TLS_shutdown(s)       ssl_close_notify(s)
+ #define TLS_close(s)  ssl_free(s); free(s)
++#define md5_hash(i, ilen, o) md5(i, ilen, o)
+ #elif defined(USE_GNUTLS)
+ #include <gnutls/gnutls.h>
+@@ -95,6 +97,8 @@ typedef struct tls_ctx {
+ #define TLS_write(s,b,l)      gnutls_record_send(s,b,l)
+ #define TLS_shutdown(s)       gnutls_bye(s, GNUTLS_SHUT_RDWR)
+ #define TLS_close(s)  gnutls_deinit(s)
++#define md5_hash(i, ilen, o) gnutls_digest_algorithm_t algorithm = GNUTLS_DIG_MD5;\
++                             gnutls_hash_fast(algorithm, i, ilen, o);
+ #else /* USE_OPENSSL */
+ #define TLS_CTX       SSL_CTX *
+@@ -105,6 +109,7 @@ typedef struct tls_ctx {
+ #define TLS_write(s,b,l)      SSL_write(s,b,l)
+ #define TLS_shutdown(s)       SSL_shutdown(s)
+ #define TLS_close(s)  SSL_free(s)
++#define md5_hash(i, ilen, o) MD5(i, ilen, o)
+ #endif
+ #endif
+diff --git a/rtmpdump.c b/rtmpdump.c
+index e52f7d4..7bb0890 100644
+--- a/rtmpdump.c
++++ b/rtmpdump.c
+@@ -701,6 +701,8 @@ void usage(char *prog)
+         RTMP_LogPrintf
+           ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
+         RTMP_LogPrintf
++          ("--weeb|-J string        Authentication token for weeb.tv servers\n");
++        RTMP_LogPrintf
+           ("--hashes|-#             Display progress with hashes, not with the byte counter\n");
+         RTMP_LogPrintf
+           ("--buffer|-b             Buffer time in milliseconds (default: %u)\n",
+@@ -747,7 +749,8 @@ main(int argc, char **argv)
+   AVal hostname = { 0, 0 };
+   AVal playpath = { 0, 0 };
+   AVal subscribepath = { 0, 0 };
+-  AVal usherToken = { 0, 0 }; //Justin.tv auth token
++  AVal usherToken = { 0, 0 }; // Justin.tv auth token
++  AVal WeebToken = { 0, 0 };  // Weeb.tv auth token
+   int port = -1;
+   int protocol = RTMP_PROTOCOL_UNDEFINED;
+   int retries = 0;
+@@ -852,12 +855,13 @@ main(int argc, char **argv)
+     {"quiet", 0, NULL, 'q'},
+     {"verbose", 0, NULL, 'V'},
+     {"jtv", 1, NULL, 'j'},
++    {"weeb", 1, NULL, 'J'},
+     {0, 0, 0, 0}
+   };
+   while ((opt =
+         getopt_long(argc, argv,
+-                    "hVveqzRr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:",
++                    "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:J:",
+                     longopts, NULL)) != -1)
+     {
+       switch (opt)
+@@ -1070,6 +1074,9 @@ main(int argc, char **argv)
+       case 'j':
+         STR2AVAL(usherToken, optarg);
+         break;
++      case 'J':
++        STR2AVAL(WeebToken, optarg);
++        break;
+       default:
+         RTMP_LogPrintf("unknown option: %c\n", opt);
+         usage(argv[0]);
+@@ -1161,14 +1168,14 @@ main(int argc, char **argv)
+   if (tcUrl.av_len == 0)
+     {
+-        tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) +
+-              hostname.av_len + app.av_len + sizeof("://:65535/");
++      tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) +
++              hostname.av_len + app.av_len + sizeof ("://:65535/");
+       tcUrl.av_val = (char *) malloc(tcUrl.av_len);
+-        if (!tcUrl.av_val)
+-          return RD_FAILED;
++      if (!tcUrl.av_val)
++        return RD_FAILED;
+       tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s",
+-                 RTMPProtocolStringsLower[protocol], hostname.av_len,
+-                 hostname.av_val, port, app.av_len, app.av_val);
++                              RTMPProtocolStringsLower[protocol], hostname.av_len,
++                              hostname.av_val, port, app.av_len, app.av_val);
+     }
+   int first = 1;
+@@ -1187,7 +1194,7 @@ main(int argc, char **argv)
+   RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
+                  &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
+-                 &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout);
++                 &flashVer, &subscribepath, &usherToken, &WeebToken, dSeek, dStopOffset, bLiveStream, timeout);
+   /* Try to keep the stream moving if it pauses on us */
+   if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP))
+diff --git a/rtmpgw.c b/rtmpgw.c
+index 0cf56bb..cd4396d 100644
+--- a/rtmpgw.c
++++ b/rtmpgw.c
+@@ -95,7 +95,8 @@ typedef struct
+   AVal flashVer;
+   AVal token;
+   AVal subscribepath;
+-  AVal usherToken; //Justin.tv auth token
++  AVal usherToken; // Justin.tv auth token
++  AVal WeebToken;  // Weeb.tv auth token
+   AVal sockshost;
+   AMFObject extras;
+   int edepth;
+@@ -553,7 +554,7 @@ void processTCPrequest(STREAMING_SERVER * server,  // server socket and state (ou
+   RTMP_Init(&rtmp);
+   RTMP_SetBufferMS(&rtmp, req.bufferTime);
+   RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost,
+-                 &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, dSeek, req.dStopOffset,
++                 &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, &req.WeebToken, dSeek, req.dStopOffset,
+                  req.bLiveStream, req.timeout);
+   /* backward compatibility, we always sent this as true before */
+   if (req.auth.av_len)
+@@ -957,6 +958,9 @@ ParseOption(char opt, char *arg, RTMP_REQUEST * req)
+     case 'j':
+       STR2AVAL(req->usherToken, arg);
+       break;
++    case 'J':
++      STR2AVAL(req->WeebToken, arg);
++      break;
+     default:
+       RTMP_LogPrintf("unknown option: %c, arg: %s\n", opt, arg);
+       return FALSE;
+@@ -1028,6 +1032,7 @@ main(int argc, char **argv)
+     {"quiet", 0, NULL, 'q'},
+     {"verbose", 0, NULL, 'V'},
+     {"jtv", 1, NULL, 'j'},
++    {"weeb", 1, NULL, 'J'},
+     {0, 0, 0, 0}
+   };
+@@ -1040,7 +1045,7 @@ main(int argc, char **argv)
+   while ((opt =
+         getopt_long(argc, argv,
+-                    "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:", longopts,
++                    "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:J:", longopts,
+                     NULL)) != -1)
+     {
+       switch (opt)
+@@ -1103,6 +1108,8 @@ main(int argc, char **argv)
+         RTMP_LogPrintf
+           ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
+         RTMP_LogPrintf
++          ("--weeb|-J string        Authentication token for weeb.tv servers\n");
++        RTMP_LogPrintf
+           ("--buffer|-b             Buffer time in milliseconds (default: %u)\n\n",
+            defaultRTMPRequest.bufferTime);
+diff --git a/rtmpsrv.c b/rtmpsrv.c
+index 9aa62f3..9ec8f23 100644
+--- a/rtmpsrv.c
++++ b/rtmpsrv.c
+@@ -96,9 +96,20 @@ STREAMING_SERVER *rtmpServer = 0;   // server structure pointer
+ STREAMING_SERVER *startStreaming(const char *address, int port);
+ void stopStreaming(STREAMING_SERVER * server);
+ void AVreplace(AVal *src, const AVal *orig, const AVal *repl);
++char *strreplace(char *srcstr, int srclen, char *orig, char *repl);
++int file_exists(const char *fname);
++int SendCheckBWResponse(RTMP *r, int oldMethodType, int onBWDoneInit);
++AVal AVcopy(AVal src);
++AVal StripParams(AVal *src);
+ static const AVal av_dquote = AVC("\"");
+ static const AVal av_escdquote = AVC("\\\"");
++#ifdef WIN32
++static const AVal av_caret = AVC("^");
++static const AVal av_esccaret = AVC("^^");
++static const AVal av_pipe = AVC("|");
++static const AVal av_escpipe = AVC("^|");
++#endif
+ typedef struct
+ {
+@@ -167,6 +178,10 @@ SAVC(level);
+ SAVC(code);
+ SAVC(description);
+ SAVC(secureToken);
++SAVC(_checkbw);
++SAVC(_onbwdone);
++SAVC(checkBandwidth);
++SAVC(onBWDone);
+ static int
+ SendConnectResult(RTMP *r, double txn)
+@@ -190,7 +205,7 @@ SendConnectResult(RTMP *r, double txn)
+   enc = AMF_EncodeNumber(enc, pend, txn);
+   *enc++ = AMF_OBJECT;
+-  STR2AVAL(av, "FMS/3,5,1,525");
++  STR2AVAL(av, "FMS/3,5,7,7009");
+   enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
+   enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
+   enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
+@@ -212,7 +227,7 @@ SendConnectResult(RTMP *r, double txn)
+   enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
+ #endif
+   STR2AVAL(p.p_name, "version");
+-  STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
++  STR2AVAL(p.p_vu.p_aval, "3,5,7,7009");
+   p.p_type = AMF_STRING;
+   obj.o_num = 1;
+   obj.o_props = &p;
+@@ -268,7 +283,7 @@ static int
+ SendPlayStart(RTMP *r)
+ {
+   RTMPPacket packet;
+-  char pbuf[512], *pend = pbuf+sizeof(pbuf);
++  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
+   packet.m_nChannel = 0x03;     // control channel (invoke)
+   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
+@@ -300,7 +315,7 @@ static int
+ SendPlayStop(RTMP *r)
+ {
+   RTMPPacket packet;
+-  char pbuf[512], *pend = pbuf+sizeof(pbuf);
++  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
+   packet.m_nChannel = 0x03;     // control channel (invoke)
+   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
+@@ -328,6 +343,49 @@ SendPlayStop(RTMP *r)
+   return RTMP_SendPacket(r, &packet, FALSE);
+ }
++int
++SendCheckBWResponse(RTMP *r, int oldMethodType, int onBWDoneInit)
++{
++  RTMPPacket packet;
++  char pbuf[256], *pend = pbuf + sizeof (pbuf);
++  char *enc;
++
++  packet.m_nChannel = 0x03; /* control channel (invoke) */
++  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
++  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
++  packet.m_nTimeStamp = 0;
++  packet.m_nInfoField2 = 0;
++  packet.m_hasAbsTimestamp = 0;
++  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
++
++  enc = packet.m_body;
++  if (oldMethodType)
++    {
++      enc = AMF_EncodeString(enc, pend, &av__onbwdone);
++      enc = AMF_EncodeNumber(enc, pend, 0);
++      *enc++ = AMF_NULL;
++      enc = AMF_EncodeNumber(enc, pend, 10240);
++      enc = AMF_EncodeNumber(enc, pend, 10240);
++    }
++  else
++    {
++      enc = AMF_EncodeString(enc, pend, &av_onBWDone);
++      enc = AMF_EncodeNumber(enc, pend, 0);
++      *enc++ = AMF_NULL;
++      if (!onBWDoneInit)
++        {
++          enc = AMF_EncodeNumber(enc, pend, 10240);
++          enc = AMF_EncodeNumber(enc, pend, 10240);
++          enc = AMF_EncodeNumber(enc, pend, 0);
++          enc = AMF_EncodeNumber(enc, pend, 0);
++        }
++    }
++
++  packet.m_nBodySize = enc - packet.m_body;
++
++  return RTMP_SendPacket(r, &packet, FALSE);
++}
++
+ static void
+ spawn_dumper(int argc, AVal *av, char *cmd)
+ {
+@@ -568,6 +626,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+         server->arglen += countAMF(&r->Link.extras, &server->argc);
+       }
+       SendConnectResult(r, txn);
++      SendCheckBWResponse(r, FALSE, TRUE);
+     }
+   else if (AVMATCH(&method, &av_createStream))
+     {
+@@ -582,10 +641,22 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+       AVal usherToken;
+       AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
+       AVreplace(&usherToken, &av_dquote, &av_escdquote);
++#ifdef WIN32
++      AVreplace(&usherToken, &av_caret, &av_esccaret);
++      AVreplace(&usherToken, &av_pipe, &av_escpipe);
++#endif
+       server->arglen += 6 + usherToken.av_len;
+       server->argc += 2;
+       r->Link.usherToken = usherToken;
+     }
++  else if (AVMATCH(&method, &av__checkbw))
++    {
++      SendCheckBWResponse(r, TRUE, FALSE);
++    }
++  else if (AVMATCH(&method, &av_checkBandwidth))
++    {
++      SendCheckBWResponse(r, FALSE, FALSE);
++    }
+   else if (AVMATCH(&method, &av_play))
+     {
+       char *file, *p, *q, *cmd, *ptr;
+@@ -599,6 +670,17 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+       if (obj.o_num > 5)
+       r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
+       */
++      double StartFlag = 0;
++      AMFObjectProperty *Start = AMF_GetProp(&obj, NULL, 4);
++      if (!(Start->p_type == AMF_INVALID))
++        StartFlag = AMFProp_GetNumber(Start);
++      r->Link.app = AVcopy(r->Link.app);
++      if (StartFlag == -1000 || strstr(r->Link.app.av_val, "live"))
++        {
++          StartFlag = -1000;
++          server->arglen += 7;
++          server->argc += 1;
++        }
+       if (r->Link.tcUrl.av_len)
+       {
+         len = server->arglen + r->Link.playpath.av_len + 4 +
+@@ -616,6 +698,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+         argv[argc].av_val = ptr + 1;
+         argv[argc++].av_len = 2;
+         argv[argc].av_val = ptr + 5;
++        r->Link.tcUrl = StripParams(&r->Link.tcUrl);
+         ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
+         argv[argc++].av_len = r->Link.tcUrl.av_len;
+@@ -640,6 +723,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+             argv[argc].av_val = ptr + 1;
+             argv[argc++].av_len = 2;
+             argv[argc].av_val = ptr + 5;
++            r->Link.swfUrl = StripParams(&r->Link.swfUrl);
+             ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
+             argv[argc++].av_len = r->Link.swfUrl.av_len;
+           }
+@@ -662,10 +746,17 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+             r->Link.usherToken.av_val = NULL;
+             r->Link.usherToken.av_len = 0;
+           }
+-        if (r->Link.extras.o_num) {
+-          ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
+-          AMF_Reset(&r->Link.extras);
+-        }
++          if (StartFlag == -1000)
++            {
++              argv[argc].av_val = ptr + 1;
++              argv[argc++].av_len = 6;
++              ptr += sprintf(ptr, " --live");
++            }
++          if (r->Link.extras.o_num)
++            {
++              ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
++              AMF_Reset(&r->Link.extras);
++            }
+         argv[argc].av_val = ptr + 1;
+         argv[argc++].av_len = 2;
+         argv[argc].av_val = ptr + 5;
+@@ -673,7 +764,13 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+           r->Link.playpath.av_len, r->Link.playpath.av_val);
+         argv[argc++].av_len = r->Link.playpath.av_len;
+-        av = r->Link.playpath;
++          if (r->Link.playpath.av_len)
++            av = r->Link.playpath;
++          else
++            {
++              av.av_val = "file";
++              av.av_len = 4;
++            }
+         /* strip trailing URL parameters */
+         q = memchr(av.av_val, '?', av.av_len);
+         if (q)
+@@ -725,7 +822,30 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+         argv[argc++].av_len = 2;
+         argv[argc].av_val = file;
+         argv[argc].av_len = av.av_len;
+-        ptr += sprintf(ptr, " -o %s", file);
++#ifdef VLC
++          char *vlc;
++          int didAlloc = FALSE;
++
++          if (getenv("VLC"))
++            vlc = getenv("VLC");
++          else if (getenv("ProgramFiles"))
++            {
++              vlc = malloc(512 * sizeof (char));
++              didAlloc = TRUE;
++              char *ProgramFiles = getenv("ProgramFiles");
++              sprintf(vlc, "%s%s", ProgramFiles, " (x86)\\VideoLAN\\VLC\\vlc.exe");
++              if (!file_exists(vlc))
++                sprintf(vlc, "%s%s", ProgramFiles, "\\VideoLAN\\VLC\\vlc.exe");
++            }
++          else
++            vlc = "C:\\Program Files\\VideoLAN\\VLC\\vlc.exe";
++
++          ptr += sprintf(ptr, " | %s -", vlc);
++          if (didAlloc)
++            free(vlc);
++#else
++          ptr += sprintf(ptr, " -o %s", file);
++#endif
+         now = RTMP_GetTime();
+         if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
+           {
+@@ -739,7 +859,23 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
+             server->filetime = now;
+             free(server->filename.av_val);
+             server->filename = argv[argc++];
+-            spawn_dumper(argc, argv, cmd);
++#ifdef VLC
++              FILE *vlc_cmdfile = fopen("VLC.bat", "w");
++              char *vlc_batchcmd = strreplace(cmd, 0, "%", "%%");
++              fprintf(vlc_cmdfile, "%s\n", vlc_batchcmd);
++              fclose(vlc_cmdfile);
++              free(vlc_batchcmd);
++              spawn_dumper(argc, argv, "VLC.bat");
++#else
++              spawn_dumper(argc, argv, cmd);
++#endif
++
++#ifdef WIN32
++              // Dump command to batch file
++              FILE *cmdfile = fopen("Command.bat", "a");
++              fprintf(cmdfile, "%s\n", cmd);
++              fclose(cmdfile);
++#endif
+           }
+         free(cmd);
+@@ -1178,3 +1314,115 @@ AVreplace(AVal *src, const AVal *orig, const AVal *repl)
+   src->av_val = dest;
+   src->av_len = dptr - dest;
+ }
++
++char *
++strreplace(char *srcstr, int srclen, char *orig, char *repl)
++{
++  char *ptr = NULL, *sptr = srcstr;
++  int origlen = strlen(orig);
++  int repllen = strlen(repl);
++  if (!srclen)
++    srclen = strlen(srcstr);
++  char *srcend = srcstr + srclen;
++  int dstbuffer = srclen / origlen * repllen;
++  if (dstbuffer < srclen)
++    dstbuffer = srclen;
++  char *dststr = calloc(dstbuffer + 1, sizeof (char));
++  char *dptr = dststr;
++
++  if ((ptr = strstr(srcstr, orig)))
++    {
++      while (ptr < srcend && (ptr = strstr(sptr, orig)))
++        {
++          int len = ptr - sptr;
++          memcpy(dptr, sptr, len);
++          sptr += len + origlen;
++          dptr += len;
++          memcpy(dptr, repl, repllen);
++          dptr += repllen;
++        }
++      memcpy(dptr, sptr, srcend - sptr);
++      return dststr;
++    }
++
++  memcpy(dststr, srcstr, srclen);
++  return dststr;
++}
++
++AVal
++StripParams(AVal *src)
++{
++  AVal str;
++  if (src->av_val)
++    {
++      str.av_val = calloc(src->av_len + 1, sizeof (char));
++      strncpy(str.av_val, src->av_val, src->av_len);
++      str.av_len = src->av_len;
++      char *start = str.av_val;
++      char *end = start + str.av_len;
++      char *ptr = start;
++
++      while (ptr < end)
++        {
++          if (*ptr == '?')
++            {
++              str.av_len = ptr - start;
++              break;
++            }
++          ptr++;
++        }
++      memset(start + str.av_len, 0, 1);
++
++      char *dynamic = strstr(start, "[[DYNAMIC]]");
++      if (dynamic)
++        {
++          dynamic -= 1;
++          memset(dynamic, 0, 1);
++          str.av_len = dynamic - start;
++          end = start + str.av_len;
++        }
++
++      char *import = strstr(start, "[[IMPORT]]");
++      if (import)
++        {
++          str.av_val = import + 11;
++          strcpy(start, "http://");
++          str.av_val = strcat(start, str.av_val);
++          str.av_len = strlen(str.av_val);
++        }
++      return str;
++    }
++  str = *src;
++  return str;
++}
++
++int
++file_exists(const char *fname)
++{
++  FILE *file;
++  if ((file = fopen(fname, "r")))
++    {
++      fclose(file);
++      return TRUE;
++    }
++  return FALSE;
++}
++
++AVal
++AVcopy(AVal src)
++{
++  AVal dst;
++  if (src.av_len)
++    {
++      dst.av_val = malloc(src.av_len + 1);
++      memcpy(dst.av_val, src.av_val, src.av_len);
++      dst.av_val[src.av_len] = '\0';
++      dst.av_len = src.av_len;
++    }
++  else
++    {
++      dst.av_val = NULL;
++      dst.av_len = 0;
++    }
++  return dst;
++}
+diff --git a/rtmpsuck.c b/rtmpsuck.c
+index e886179..e80c686 100644
+--- a/rtmpsuck.c
++++ b/rtmpsuck.c
+@@ -143,15 +143,18 @@ SAVC(onStatus);
+ SAVC(close);
+ static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
+ static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed");
+-static const AVal av_NetStream_Play_StreamNotFound =
+-AVC("NetStream.Play.StreamNotFound");
+-static const AVal av_NetConnection_Connect_InvalidApp =
+-AVC("NetConnection.Connect.InvalidApp");
++static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound");
++static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp");
+ static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
+ static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete");
+ static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
++static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
+ static const char *cst[] = { "client", "server" };
++char *dumpAMF(AMFObject *obj, char *ptr);
++char *strreplace(char *srcstr, int srclen, char *orig, char *repl);
++AVal AVcopy(AVal src);
++AVal StripParams(AVal *src);
+ // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
+ int
+@@ -198,26 +201,28 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
+           if (cobj.o_props[i].p_type == AMF_STRING)
+             {
+               pval = cobj.o_props[i].p_vu.p_aval;
+-              RTMP_LogPrintf("%.*s: %.*s\n", pname.av_len, pname.av_val, pval.av_len, pval.av_val);
++              RTMP_LogPrintf("%10.*s : %.*s\n", pname.av_len, pname.av_val, pval.av_len, pval.av_val);
+             }
+           if (AVMATCH(&pname, &av_app))
+             {
+-              server->rc.Link.app = pval;
++              server->rc.Link.app = AVcopy(pval);
+               pval.av_val = NULL;
+             }
+           else if (AVMATCH(&pname, &av_flashVer))
+             {
+-              server->rc.Link.flashVer = pval;
++              server->rc.Link.flashVer = AVcopy(pval);
+               pval.av_val = NULL;
+             }
+           else if (AVMATCH(&pname, &av_swfUrl))
+             {
+ #ifdef CRYPTO
+               if (pval.av_val)
+-              RTMP_HashSWF(pval.av_val, &server->rc.Link.SWFSize,
+-                (unsigned char *)server->rc.Link.SWFHash, 30);
++                {
++                  AVal swfUrl = StripParams(&pval);
++                  RTMP_HashSWF(swfUrl.av_val, &server->rc.Link.SWFSize, (unsigned char *) server->rc.Link.SWFHash, 30);
++                }
+ #endif
+-              server->rc.Link.swfUrl = pval;
++              server->rc.Link.swfUrl = AVcopy(pval);
+               pval.av_val = NULL;
+             }
+           else if (AVMATCH(&pname, &av_tcUrl))
+@@ -225,7 +230,7 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
+               char *r1 = NULL, *r2;
+               int len;
+-              server->rc.Link.tcUrl = pval;
++              server->rc.Link.tcUrl = AVcopy(pval);
+               if ((pval.av_val[0] | 0x40) == 'r' &&
+                   (pval.av_val[1] | 0x40) == 't' &&
+                   (pval.av_val[2] | 0x40) == 'm' &&
+@@ -267,7 +272,7 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
+             }
+           else if (AVMATCH(&pname, &av_pageUrl))
+             {
+-              server->rc.Link.pageUrl = pval;
++              server->rc.Link.pageUrl = AVcopy(pval);
+               pval.av_val = NULL;
+             }
+           else if (AVMATCH(&pname, &av_audioCodecs))
+@@ -287,14 +292,21 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
+           if (pval.av_val)
+             free(pval.av_val);
+         }
++
+       if (obj.o_num > 3)
+         {
+-          if (AMFProp_GetBoolean(&obj.o_props[3]))
+-            server->rc.Link.lFlags |= RTMP_LF_AUTH;
+-          if (obj.o_num > 4)
+-          {
+-            AMFProp_GetString(&obj.o_props[4], &server->rc.Link.auth);
+-          }
++          int i = obj.o_num - 3;
++          server->rc.Link.extras.o_num = i;
++          server->rc.Link.extras.o_props = malloc(i * sizeof (AMFObjectProperty));
++          memcpy(server->rc.Link.extras.o_props, obj.o_props + 3, i * sizeof (AMFObjectProperty));
++          obj.o_num = 3;
++        }
++
++      if (server->rc.Link.extras.o_num)
++        {
++          server->rc.Link.Extras.av_val = calloc(1024, sizeof (char));
++          dumpAMF(&server->rc.Link.extras, server->rc.Link.Extras.av_val);
++          server->rc.Link.Extras.av_len = strlen(server->rc.Link.Extras.av_val);
+         }
+       if (!RTMP_Connect(&server->rc, pack))
+@@ -303,6 +315,16 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
+           return 1;
+         }
+       server->rc.m_bSendCounter = FALSE;
++
++      if (server->rc.Link.extras.o_props)
++        {
++          AMF_Reset(&server->rc.Link.extras);
++        }
++    }
++  else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
++    {
++      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &server->rc.Link.usherToken);
++      RTMP_LogPrintf("%10s : %.*s\n", "usherToken", server->rc.Link.usherToken.av_len, server->rc.Link.usherToken.av_val);
+     }
+   else if (AVMATCH(&method, &av_play))
+     {
+@@ -323,6 +345,14 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
+       if (!av.av_val)
+         goto out;
++      double StartFlag = 0;
++      AMFObjectProperty *Start = AMF_GetProp(&obj, NULL, 4);
++      if (!(Start->p_type == AMF_INVALID))
++        StartFlag = AMFProp_GetNumber(Start);
++      if (StartFlag == -1000 || strstr(server->rc.Link.app.av_val, "live"))
++        StartFlag = -1000;
++      RTMP_LogPrintf("%10s : %s\n", "live", (StartFlag == -1000) ? "yes" : "no");
++
+       /* check for duplicates */
+       for (fl = server->f_head; fl; fl=fl->f_next)
+         {
+@@ -372,9 +402,51 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
+       for (p=file; *p; p++)
+         if (*p == ':')
+           *p = '_';
+-      RTMP_LogPrintf("Playpath: %.*s\nSaving as: %s\n",
+-        server->rc.Link.playpath.av_len, server->rc.Link.playpath.av_val,
+-        file);
++      RTMP_LogPrintf("%10s : %.*s\n%10s : %s\n", "Playpath", server->rc.Link.playpath.av_len,
++                     server->rc.Link.playpath.av_val, "Saving as", file);
++
++#ifdef WIN32
++      // Dump command to batch file
++      char *cmd = NULL, *ptr = NULL;
++      AVal swfUrl, tcUrl;
++
++      cmd = calloc(2048, sizeof (char));
++      ptr = cmd;
++      tcUrl = StripParams(&server->rc.Link.tcUrl);
++      swfUrl = StripParams(&server->rc.Link.swfUrl);
++      ptr += sprintf(ptr, "rtmpdump -r \"%.*s\" -a \"%.*s\" -f \"%.*s\" -W \"%.*s\" -p \"%.*s\"",
++                     tcUrl.av_len, tcUrl.av_val,
++                     server->rc.Link.app.av_len, server->rc.Link.app.av_val,
++                     server->rc.Link.flashVer.av_len, server->rc.Link.flashVer.av_val,
++                     swfUrl.av_len, swfUrl.av_val,
++                     server->rc.Link.pageUrl.av_len, server->rc.Link.pageUrl.av_val);
++
++      if (server->rc.Link.usherToken.av_val)
++        {
++          char *usherToken = strreplace(server->rc.Link.usherToken.av_val, server->rc.Link.usherToken.av_len, "\"", "\\\"");
++          usherToken = strreplace(usherToken, 0, "^", "^^");
++          usherToken = strreplace(usherToken, 0, "|", "^|");
++          ptr += sprintf(ptr, " --jtv \"%s\"", usherToken);
++          free(usherToken);
++        }
++
++      if (server->rc.Link.Extras.av_len)
++        {
++          ptr += sprintf(ptr, "%.*s", server->rc.Link.Extras.av_len, server->rc.Link.Extras.av_val);
++        }
++
++      if (StartFlag == -1000)
++        ptr += sprintf(ptr, "%s", " --live");
++      ptr += sprintf(ptr, " -y \"%.*s\"", server->rc.Link.playpath.av_len, server->rc.Link.playpath.av_val);
++      ptr += sprintf(ptr, " -o \"%s.flv\"\n", file);
++
++      FILE *cmdfile = fopen("Command.bat", "a");
++      fprintf(cmdfile, "%s", cmd);
++      fclose(cmdfile);
++
++      free(cmd);
++#endif
++
+       out = fopen(file, "wb");
+       free(file);
+       if (!out)
+@@ -1196,3 +1268,146 @@ main(int argc, char **argv)
+ #endif
+   return nStatus;
+ }
++
++char *
++dumpAMF(AMFObject *obj, char *ptr)
++{
++  int i;
++  const char opt[] = "NBSO Z";
++
++  for (i = 0; i < obj->o_num; i++)
++    {
++      AMFObjectProperty *p = &obj->o_props[i];
++      if (p->p_type > 5)
++        continue;
++      ptr += sprintf(ptr, " -C ");
++      if (p->p_name.av_val)
++        *ptr++ = 'N';
++      *ptr++ = opt[p->p_type];
++      *ptr++ = ':';
++      if (p->p_name.av_val)
++        ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val);
++      switch (p->p_type)
++        {
++        case AMF_BOOLEAN:
++          *ptr++ = p->p_vu.p_number != 0 ? '1' : '0';
++          break;
++        case AMF_STRING:
++          memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len);
++          ptr += p->p_vu.p_aval.av_len;
++          break;
++        case AMF_NUMBER:
++          ptr += sprintf(ptr, "%f", p->p_vu.p_number);
++          break;
++        case AMF_OBJECT:
++          *ptr++ = '1';
++          ptr = dumpAMF(&p->p_vu.p_object, ptr);
++          ptr += sprintf(ptr, " -C O:0");
++          break;
++        case AMF_NULL:
++        default:
++          break;
++        }
++    }
++  return ptr;
++}
++
++char *
++strreplace(char *srcstr, int srclen, char *orig, char *repl)
++{
++  char *ptr = NULL, *sptr = srcstr;
++  int origlen = strlen(orig);
++  int repllen = strlen(repl);
++  if (!srclen)
++    srclen = strlen(srcstr);
++  char *srcend = srcstr + srclen;
++  int dstbuffer = srclen / origlen * repllen;
++  if (dstbuffer < srclen)
++    dstbuffer = srclen;
++  char *dststr = calloc(dstbuffer + 1, sizeof (char));
++  char *dptr = dststr;
++
++  if ((ptr = strstr(srcstr, orig)))
++    {
++      while (ptr < srcend && (ptr = strstr(sptr, orig)))
++        {
++          int len = ptr - sptr;
++          memcpy(dptr, sptr, len);
++          sptr += len + origlen;
++          dptr += len;
++          memcpy(dptr, repl, repllen);
++          dptr += repllen;
++        }
++      memcpy(dptr, sptr, srcend - sptr);
++      return dststr;
++    }
++
++  memcpy(dststr, srcstr, srclen);
++  return dststr;
++}
++
++AVal
++StripParams(AVal *src)
++{
++  AVal str;
++  if (src->av_val)
++    {
++      str.av_val = calloc(src->av_len + 1, sizeof (char));
++      strncpy(str.av_val, src->av_val, src->av_len);
++      str.av_len = src->av_len;
++      char *start = str.av_val;
++      char *end = start + str.av_len;
++      char *ptr = start;
++
++      while (ptr < end)
++        {
++          if (*ptr == '?')
++            {
++              str.av_len = ptr - start;
++              break;
++            }
++          ptr++;
++        }
++      memset(start + str.av_len, 0, 1);
++
++      char *dynamic = strstr(start, "[[DYNAMIC]]");
++      if (dynamic)
++        {
++          dynamic -= 1;
++          memset(dynamic, 0, 1);
++          str.av_len = dynamic - start;
++          end = start + str.av_len;
++        }
++
++      char *import = strstr(start, "[[IMPORT]]");
++      if (import)
++        {
++          str.av_val = import + 11;
++          strcpy(start, "http://");
++          str.av_val = strcat(start, str.av_val);
++          str.av_len = strlen(str.av_val);
++        }
++      return str;
++    }
++  str = *src;
++  return str;
++}
++
++AVal
++AVcopy(AVal src)
++{
++  AVal dst;
++  if (src.av_len)
++    {
++      dst.av_val = malloc(src.av_len + 1);
++      memcpy(dst.av_val, src.av_val, src.av_len);
++      dst.av_val[src.av_len] = '\0';
++      dst.av_len = src.av_len;
++    }
++  else
++    {
++      dst.av_val = NULL;
++      dst.av_len = 0;
++    }
++  return dst;
++}
index 07a7ee0..26bff3c 100644 (file)
@@ -2,7 +2,7 @@ include ../Makefile.include
 DEPS= ../Makefile.include Makefile
 
 LIBNAME=xbmc-pvr-addons
-VERSION=5f97406cffb412ac5161c3dc51205caca009fcc7
+VERSION=96774c4f775b156a46fb58151379dece3e773c96
 GIT_DIR=$(TARBALLS_LOCATION)/$(LIBNAME).git
 BASE_URL=git://github.com/opdenkamp/$(LIBNAME).git
 DYLIB=$(PLATFORM)/addons/pvr.demo/.libs/libpvrdemo-addon.so
index 220fc26..c58d670 100644 (file)
@@ -5,7 +5,7 @@ XBMC_ADDONSDIR=../../../../addons
 
 # lib name, version
 LIBNAME=xbmc-pvr-addons
-VERSION=5f97406cffb412ac5161c3dc51205caca009fcc7
+VERSION=96774c4f775b156a46fb58151379dece3e773c96
 SOURCE=$(LIBNAME)-$(VERSION)
 ARCHIVE=$(SOURCE).tar.gz
 
index 4c79137..d5db046 100644 (file)
 #include "input/SDLJoystick.h"
 #endif
 
+#if defined(TARGET_ANDROID)
+#include "android/activity/XBMCApp.h"
+#endif
+
 using namespace std;
 using namespace ADDON;
 using namespace XFILE;
@@ -632,17 +636,17 @@ bool CApplication::Create()
   CLog::Log(LOGNOTICE, "Starting XBMC (%s), Platform: Linux (%s, %s). Built on %s", g_infoManager.GetVersion().c_str(), g_sysinfo.GetLinuxDistro().c_str(), g_sysinfo.GetUnameVersion().c_str(), __DATE__);
 #elif defined(_WIN32)
   CLog::Log(LOGNOTICE, "Starting XBMC (%s), Platform: %s. Built on %s (compiler %i)", g_infoManager.GetVersion().c_str(), g_sysinfo.GetKernelVersion().c_str(), __DATE__, _MSC_VER);
+  CLog::Log(LOGNOTICE, g_cpuInfo.getCPUModel().c_str());
+  CLog::Log(LOGNOTICE, CWIN32Util::GetResInfoString());
+  CLog::Log(LOGNOTICE, "Running with %s rights", (CWIN32Util::IsCurrentUserLocalAdministrator() == TRUE) ? "administrator" : "restricted");
+  CLog::Log(LOGNOTICE, "Aero is %s", (g_sysinfo.IsAeroDisabled() == true) ? "disabled" : "enabled");
+#endif
 #if defined(__arm__)
   if (g_cpuInfo.GetCPUFeatures() & CPU_FEATURE_NEON)
     CLog::Log(LOGNOTICE, "ARM Features: Neon enabled");
   else
     CLog::Log(LOGNOTICE, "ARM Features: Neon disabled");
 #endif
-  CLog::Log(LOGNOTICE, g_cpuInfo.getCPUModel().c_str());
-  CLog::Log(LOGNOTICE, CWIN32Util::GetResInfoString());
-  CLog::Log(LOGNOTICE, "Running with %s rights", (CWIN32Util::IsCurrentUserLocalAdministrator() == TRUE) ? "administrator" : "restricted");
-  CLog::Log(LOGNOTICE, "Aero is %s", (g_sysinfo.IsAeroDisabled() == true) ? "disabled" : "enabled");
-#endif
   CSpecialProtocol::LogPaths();
 
   CStdString executable = CUtil::ResolveExecutablePath();
@@ -2859,15 +2863,19 @@ bool CApplication::OnAction(const CAction &action)
       if (g_settings.m_bMute)
         UnMute();
       float volume = g_settings.m_fVolumeLevel;
+// Android has steps based on the max available volume level
+#if defined(TARGET_ANDROID)
+      float step = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / CXBMCApp::GetMaxSystemVolume();
+#else
       float step   = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / VOLUME_CONTROL_STEPS;
+
       if (action.GetRepeat())
         step *= action.GetRepeat() * 50; // 50 fps
-
+#endif
       if (action.GetID() == ACTION_VOLUME_UP)
         volume += (float)fabs(action.GetAmount()) * action.GetAmount() * step;
       else
         volume -= (float)fabs(action.GetAmount()) * action.GetAmount() * step;
-
       SetVolume(volume, false);
     }
     // show visual feedback of volume change...
@@ -4117,6 +4125,13 @@ bool CApplication::PlayFile(const CFileItem& item, bool bRestart)
       SetPlaySpeed(iSpeed);
     }
 
+    // if player has volume control, set it.
+    if (m_pPlayer && m_pPlayer->ControlsVolume())
+    {
+       m_pPlayer->SetVolume(g_settings.m_fVolumeLevel);
+       m_pPlayer->SetMute(g_settings.m_bMute);
+    }
+
     if( IsPlayingAudio() )
     {
       if (g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
@@ -5383,10 +5398,6 @@ void CApplication::SetHardwareVolume(float hardwareVolume)
     value = 1.0f;
 
   CAEFactory::SetVolume(value);
-
-  /* for platforms where we do not have AE */
-  if (!CAEFactory::GetEngine() && m_pPlayer)
-    m_pPlayer->SetVolume(g_settings.m_fVolumeLevel);
 }
 
 int CApplication::GetVolume() const
@@ -5401,6 +5412,13 @@ void CApplication::VolumeChanged() const
   data["volume"] = GetVolume();
   data["muted"] = g_settings.m_bMute;
   CAnnouncementManager::Announce(Application, "xbmc", "OnVolumeChanged", data);
+
+  // if player has volume control, set it.
+  if (m_pPlayer && m_pPlayer->ControlsVolume())
+  {
+     m_pPlayer->SetVolume(g_settings.m_fVolumeLevel);
+     m_pPlayer->SetMute(g_settings.m_bMute);
+  }
 }
 
 int CApplication::GetSubtitleDelay() const
@@ -5437,13 +5455,19 @@ void CApplication::SetPlaySpeed(int iSpeed)
   m_iPlaySpeed = iSpeed;
 
   m_pPlayer->ToFFRW(m_iPlaySpeed);
-  if (m_iPlaySpeed == 1)
-  { // restore volume
-    m_pPlayer->SetVolume(VOLUME_MAXIMUM);
-  }
-  else
-  { // mute volume
-    m_pPlayer->SetVolume(VOLUME_MINIMUM);
+
+  // if player has volume control, set it.
+  if (m_pPlayer->ControlsVolume())
+  {
+    if (m_iPlaySpeed == 1)
+    { // restore volume
+      m_pPlayer->SetVolume(VOLUME_MAXIMUM);
+    }
+    else
+    { // mute volume
+      m_pPlayer->SetVolume(VOLUME_MINIMUM);
+    }
+    m_pPlayer->SetMute(g_settings.m_bMute);
   }
 }
 
index 595b285..69609fa 100644 (file)
@@ -378,14 +378,15 @@ protected:
   // timer information
 #ifdef _WIN32
   CWinIdleTimer m_idleTimer;
+  CWinIdleTimer m_screenSaverTimer;
 #else
   CStopWatch m_idleTimer;
+  CStopWatch m_screenSaverTimer;
 #endif
   CStopWatch m_restartPlayerTimer;
   CStopWatch m_frameTime;
   CStopWatch m_navigationTimer;
   CStopWatch m_slowTimer;
-  CStopWatch m_screenSaverTimer;
   CStopWatch m_shutdownTimer;
 
   bool m_bInhibitIdleShutdown;
index 988b887..59c2ee2 100644 (file)
@@ -468,9 +468,19 @@ void CApplicationMessenger::ProcessMessage(ThreadMessage *pMsg)
     case TMSG_MEDIA_STOP:
       {
         // restore to previous window if needed
-        if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW ||
-            g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO ||
-            g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION)
+        bool stopSlideshow = true;
+        bool stopVideo = true;
+        bool stopMusic = true;
+        if (pMsg->dwParam1 >= 0)
+        {
+          stopSlideshow = (pMsg->dwParam1 == PLAYLIST_PICTURE);
+          stopVideo = (pMsg->dwParam1 == PLAYLIST_VIDEO);
+          stopMusic = (pMsg->dwParam1 == PLAYLIST_MUSIC);
+        }
+
+        if ((stopSlideshow && g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) ||
+            (stopVideo && g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO) ||
+            (stopMusic && g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION))
           g_windowManager.PreviousWindow();
 
         g_application.ResetScreenSaver();
@@ -890,9 +900,10 @@ void CApplicationMessenger::PlayFile(const CFileItem &item, bool bRestart /*= fa
   SendMessage(tMsg, false);
 }
 
-void CApplicationMessenger::MediaStop(bool bWait /* = true */)
+void CApplicationMessenger::MediaStop(bool bWait /* = true */, int playlistid /* = -1 */)
 {
   ThreadMessage tMsg = {TMSG_MEDIA_STOP};
+  tMsg.dwParam1 = playlistid;
   SendMessage(tMsg, bWait);
 }
 
index 625a5b3..0bceaaa 100644 (file)
@@ -165,7 +165,7 @@ public:
   void MediaPlay(const CFileItem &item);
   void MediaPlay(const CFileItemList &item, int song = 0);
   void MediaPlay(int playlistid, int song = -1);
-  void MediaStop(bool bWait = true);
+  void MediaStop(bool bWait = true, int playlistid = -1);
   void MediaPause();
   void MediaRestart(bool bWait);
 
index 2b69129..99e3d87 100644 (file)
@@ -2789,7 +2789,7 @@ CStdString CFileItem::GetLocalArt(const std::string &artFile, bool useFolder) co
     useFolder = true;
     strFile = GetLocalMetadataPath();
   }
-  else if (useFolder)
+  else if (useFolder && !(m_bIsFolder && !IsFileFolder()))
     strFile = URIUtils::GetDirectory(strFile);
 
   if (strFile.empty()) // empty filepath -> nothing to find
index 79d69bc..b3cb33e 100644 (file)
@@ -80,7 +80,7 @@ static const TypeMapping types[] =
    {"xbmc.gui.skin",                     ADDON_SKIN,                  166, "DefaultAddonSkin.png" },
    {"xbmc.gui.webinterface",             ADDON_WEB_INTERFACE,         199, "DefaultAddonWebSkin.png" },
    {"xbmc.addon.repository",             ADDON_REPOSITORY,          24011, "DefaultAddonRepository.png" },
-   {"xbmc.pvrclient",                    ADDON_PVRDLL,              24019, "" },
+   {"xbmc.pvrclient",                    ADDON_PVRDLL,              24019, "DefaultAddonPVRClient.png" },
    {"xbmc.addon.video",                  ADDON_VIDEO,                1037, "DefaultAddonVideo.png" },
    {"xbmc.addon.audio",                  ADDON_AUDIO,                1038, "DefaultAddonMusic.png" },
    {"xbmc.addon.image",                  ADDON_IMAGE,                1039, "DefaultAddonPicture.png" },
index 3a2ee01..cb382aa 100644 (file)
@@ -566,12 +566,9 @@ bool CAddonDatabase::DisableAddon(const CStdString &addonID, bool disable /* = t
 
     if (disable)
     {
-      CStdString sql = PrepareSQL("select id from disabled where addonID='%s'", addonID.c_str());
-      m_pDS->query(sql.c_str());
-      if (m_pDS->eof()) // not found
+      if (!IsAddonDisabled(addonID)) // Enabled
       {
-        m_pDS->close();
-        sql = PrepareSQL("insert into disabled(id, addonID) values(NULL, '%s')", addonID.c_str());
+        CStdString sql = PrepareSQL("insert into disabled(id, addonID) values(NULL, '%s')", addonID.c_str());
         m_pDS->exec(sql);
 
         AddonPtr addon;
@@ -593,12 +590,13 @@ bool CAddonDatabase::DisableAddon(const CStdString &addonID, bool disable /* = t
     }
     else
     {
+      bool disabled = IsAddonDisabled(addonID); //we need to know if service addon is running
       CStdString sql = PrepareSQL("delete from disabled where addonID='%s'", addonID.c_str());
       m_pDS->exec(sql);
 
       AddonPtr addon;
       // If the addon is a service, start it
-      if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) && addon)
+      if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) && addon && disabled)
       {
         boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
         if (service)
index 1d7614e..911a6ee 100644 (file)
@@ -556,10 +556,18 @@ bool CAddonInstallJob::OnPreInstall()
 
   if (m_addon->Type() == ADDON_SERVICE)
   {
-    boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(m_addon);
+    CAddonDatabase database;
+    database.Open();
+    bool running = !database.IsAddonDisabled(m_addon->ID()); //grab a current state
+    database.DisableAddon(m_addon->ID(),false); // enable it so we can remove it??
+    // regrab from manager to have the correct path set
+    AddonPtr addon;
+    ADDON::CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
+    boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
     if (service)
       service->Stop();
-    return true;
+    CAddonMgr::Get().RemoveAddon(m_addon->ID()); // remove it
+    return running;
   }
 
   if (m_addon->Type() == ADDON_PVRDLL)
@@ -667,12 +675,18 @@ void CAddonInstallJob::OnPostInstall(bool reloadAddon)
 
   if (m_addon->Type() == ADDON_SERVICE)
   {
-    // regrab from manager to have the correct path set
-    AddonPtr addon; 
-    CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
-    boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
-    if (service)
-      service->Start();
+    CAddonDatabase database;
+    database.Open();
+    database.DisableAddon(m_addon->ID(),!reloadAddon); //return it into state it was before OnPreInstall()
+    if (reloadAddon) // reload/start it if it was running
+    {
+      // regrab from manager to have the correct path set
+      AddonPtr addon; 
+      CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
+      boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
+      if (service)
+        service->Start();
+    }
   }
 
   if (m_addon->Type() == ADDON_REPOSITORY)
@@ -746,7 +760,12 @@ bool CAddonUnInstallJob::DoWork()
     // stop the pvr manager, so running pvr add-ons are stopped and closed
     PVR::CPVRManager::Get().Stop();
   }
-
+  if (m_addon->Type() == ADDON_SERVICE)
+  {
+    boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(m_addon);
+    if (service)
+      service->Stop();
+  }
   if (!CAddonInstallJob::DeleteAddon(m_addon->Path()))
     return false;
 
index a2275be..9c95424 100644 (file)
@@ -296,7 +296,9 @@ void CGUIDialogAddonInfo::OnRollback()
     CStdString path = "special://home/addons/packages/";
     path += m_localAddon->ID()+"-"+m_rollbackVersions[choice]+".zip";
     // needed as cpluff won't downgrade
-    CAddonMgr::Get().RemoveAddon(m_localAddon->ID());
+    if (!m_localAddon->IsType(ADDON_SERVICE))
+      //we will handle this for service addons in CAddonInstallJob::OnPostInstall
+      CAddonMgr::Get().RemoveAddon(m_localAddon->ID());
     CAddonInstaller::Get().InstallFromZip(path);
     database.RemoveAddonFromBlacklist(m_localAddon->ID(),m_rollbackVersions[choice]);
     Close();
index 88139e2..5922ca3 100644 (file)
@@ -127,7 +127,6 @@ ActivityResult CXBMCApp::onActivate()
     default:
       break;
   }
-  
   return ActivityOK;
 }
 
@@ -1042,3 +1041,71 @@ bool CXBMCApp::GetStorageUsage(const std::string &path, std::string &usage)
   usage = fmt.str();
   return true;
 }
+
+// Used in Application.cpp to figure out volume steps
+int CXBMCApp::GetMaxSystemVolume()
+{
+  static int maxVolume = -1;
+  if (maxVolume == -1)
+  {
+    JNIEnv *env = NULL;
+    AttachCurrentThread(&env);
+    maxVolume = GetMaxSystemVolume(env);
+    DetachCurrentThread();
+  }
+  return maxVolume;
+}
+
+int CXBMCApp::GetMaxSystemVolume(JNIEnv *env)
+{
+  jobject oActivity = m_activity->clazz;
+  jclass cActivity = env->GetObjectClass(oActivity);
+
+  // Get Audio manager
+  //  (AudioManager)getSystemService(Context.AUDIO_SERVICE)
+  jmethodID mgetSystemService = env->GetMethodID(cActivity, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
+  jstring sAudioService = env->NewStringUTF("audio");
+  jobject oAudioManager = env->CallObjectMethod(oActivity, mgetSystemService, sAudioService);
+  env->DeleteLocalRef(sAudioService);
+  env->DeleteLocalRef(cActivity);
+
+  // Get max volume
+  //  int max_volume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+  jclass cAudioManager = env->GetObjectClass(oAudioManager);
+  jmethodID mgetStreamMaxVolume = env->GetMethodID(cAudioManager, "getStreamMaxVolume", "(I)I");
+  jfieldID fstreamMusic = env->GetStaticFieldID(cAudioManager, "STREAM_MUSIC", "I");
+  jint stream_music = env->GetStaticIntField(cAudioManager, fstreamMusic);
+  int maxVolume = (int)env->CallObjectMethod(oAudioManager, mgetStreamMaxVolume, stream_music); // AudioManager.STREAM_MUSIC
+
+  env->DeleteLocalRef(oAudioManager);
+  env->DeleteLocalRef(cAudioManager);
+
+  return maxVolume;
+}
+
+void CXBMCApp::SetSystemVolume(JNIEnv *env, float percent)
+{
+  CLog::Log(LOGDEBUG, "CXBMCApp::SetSystemVolume: %f", percent);
+
+  jobject oActivity = m_activity->clazz;
+  jclass cActivity = env->GetObjectClass(oActivity);
+
+  // Get Audio manager
+  //  (AudioManager)getSystemService(Context.AUDIO_SERVICE)
+  jmethodID mgetSystemService = env->GetMethodID(cActivity, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
+  jstring sAudioService = env->NewStringUTF("audio");
+  jobject oAudioManager = env->CallObjectMethod(oActivity, mgetSystemService, sAudioService);
+  jclass cAudioManager = env->GetObjectClass(oAudioManager);
+  env->DeleteLocalRef(sAudioService);
+  env->DeleteLocalRef(cActivity);
+
+  // Set volume
+  //   mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, max_volume, 0);
+  jfieldID fstreamMusic = env->GetStaticFieldID(cAudioManager, "STREAM_MUSIC", "I");
+  jint stream_music = env->GetStaticIntField(cAudioManager, fstreamMusic);
+  jmethodID msetStreamVolume = env->GetMethodID(cAudioManager, "setStreamVolume", "(III)V");
+  env->CallObjectMethod(oAudioManager, msetStreamVolume, stream_music, int(GetMaxSystemVolume(env)*percent), 0);
+  env->DeleteLocalRef(oAudioManager);
+  env->DeleteLocalRef(cAudioManager);
+}
+
index b08b6df..d9f728f 100644 (file)
@@ -87,6 +87,7 @@ public:
   static bool ListApplications(std::vector <androidPackage> *applications);
   static bool GetIconSize(const std::string &packageName, int *width, int *height);
   static bool GetIcon(const std::string &packageName, void* buffer, unsigned int bufSize); 
+
   /*!
    * \brief If external storage is available, it returns the path for the external storage (for the specified type)
    * \param path will contain the path of the external storage (for the specified type)
@@ -95,6 +96,7 @@ public:
    */
   static bool GetExternalStorage(std::string &path, const std::string &type = "");
   static bool GetStorageUsage(const std::string &path, std::string &usage);
+  static int GetMaxSystemVolume();
 
   static int GetDPI();
 protected:
@@ -106,6 +108,9 @@ protected:
   static int AttachCurrentThread(JNIEnv** p_env, void* thr_args = NULL);
   static int DetachCurrentThread();
 
+  static int GetMaxSystemVolume(JNIEnv *env);
+  static void SetSystemVolume(JNIEnv *env, float percent);
+
 private:
   static bool HasLaunchIntent(const std::string &package);
   bool getWakeLock(JNIEnv *env);
@@ -116,7 +121,6 @@ private:
 
   static ANativeActivity *m_activity;
   jobject m_wakeLock;
-  
   typedef enum {
     // XBMC_Initialize hasn't been executed yet
     Uninitialized,
index c6e0309..944fa97 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "CoreAudioAEStream.h"
 #include "CoreAudioAESound.h"
+#include "CoreAudioHardware.h"
 #include "cores/AudioEngine/Utils/AEUtil.h"
 #include "settings/GUISettings.h"
 #include "settings/Settings.h"
@@ -33,6 +34,7 @@
 #include "utils/log.h"
 #include "utils/TimeUtils.h"
 #include "utils/MathUtils.h"
+#include "threads/SystemClock.h"
 
 #define DELAY_FRAME_TIME  20
 #define BUFFERSIZE        16416
@@ -50,7 +52,10 @@ CCoreAudioAE::CCoreAudioAE() :
   m_muted              (false         ),
   m_soundMode          (AE_SOUND_OFF  ),
   m_streamsPlaying     (false         ),
-  m_isSuspended        (false         )
+  m_isSuspended        (false         ),
+  m_softSuspend        (false         ),
+  m_softSuspendTimer   (0             ),
+  m_currentAudioDevice (0             )
 {
   HAL = new CCoreAudioAEHAL;
 }
@@ -269,6 +274,11 @@ bool CCoreAudioAE::OpenCoreAudio(unsigned int sampleRate, bool forceRaw,
       (*itt)->Initialize();
     streamLock.Leave();
   }
+  
+  if (m_Initialized)
+  {
+    m_currentAudioDevice = CCoreAudioHardware::FindAudioDevice(m_outputDevice);
+  }
 
   return m_Initialized;
 }
@@ -426,9 +436,10 @@ CCoreAudioAEHAL* CCoreAudioAE::GetHAL()
 IAEStream* CCoreAudioAE::MakeStream(enum AEDataFormat dataFormat,
   unsigned int sampleRate, unsigned int encodedSamplerate, CAEChannelInfo channelLayout, unsigned int options)
 {
+  bool defaultDeviceChanged = false;
   // if we are suspended we don't
   // want anyone to mess with us
-  if (m_isSuspended)
+  if (m_isSuspended && !m_softSuspend)
     return NULL;
 
   CAEChannelInfo channelInfo(channelLayout);
@@ -440,6 +451,18 @@ IAEStream* CCoreAudioAE::MakeStream(enum AEDataFormat dataFormat,
   m_streams.push_back(stream);
   streamLock.Leave();
 
+  // check if default device has changed - in that case we need to reinit
+  // TODO hook into osx callbacks for getting notifiaction on device
+  // changes and then queue a change of the default device
+  // to a point where engine is idle.
+  std::string outputDevice = g_guiSettings.GetString("audiooutput.audiodevice");
+  if (outputDevice.compare("CoreAudio:default") == 0)
+  {
+    AudioDeviceID currentId = CCoreAudioHardware::FindAudioDevice("default");
+    if (currentId != m_currentAudioDevice)
+      defaultDeviceChanged = true;
+  }
+
   if ((options & AESTREAM_PAUSED) == 0)
     Stop();
 
@@ -447,7 +470,8 @@ IAEStream* CCoreAudioAE::MakeStream(enum AEDataFormat dataFormat,
   if (m_Initialized && ( m_lastStreamFormat != dataFormat ||
                          m_lastChLayoutCount != channelLayout.Count() ||
                          m_lastSampleRate != sampleRate ||
-                         COREAUDIO_IS_RAW(dataFormat)))
+                         COREAUDIO_IS_RAW(dataFormat) ||
+                         defaultDeviceChanged))
   {
     CSingleLock engineLock(m_engineLock);
     Stop();
@@ -510,7 +534,7 @@ IAEStream* CCoreAudioAE::FreeStream(IAEStream *stream)
 
 void CCoreAudioAE::PlaySound(IAESound *sound)
 {
-  if (m_soundMode == AE_SOUND_OFF || (m_soundMode == AE_SOUND_IDLE && m_streamsPlaying) || m_isSuspended)
+  if (m_soundMode == AE_SOUND_OFF || (m_soundMode == AE_SOUND_IDLE && m_streamsPlaying) || (m_isSuspended && !m_softSuspend))
     return;
 
   float *samples = ((CCoreAudioAESound*)sound)->GetSamples();
@@ -632,11 +656,40 @@ void CCoreAudioAE::MixSounds(float *buffer, unsigned int samples)
 
 void CCoreAudioAE::GarbageCollect()
 {
+  if (g_advancedSettings.m_streamSilence)
+    return;
+  
+  if (!m_streamsPlaying && m_playing_sounds.empty())
+  {
+    if (!m_softSuspend)
+    {
+      m_softSuspend = true;
+      m_softSuspendTimer = XbmcThreads::SystemClockMillis() + 10000; //10.0 second delay for softSuspend
+    }
+  }
+  else
+  {
+    if (m_isSuspended)
+    {
+      CSingleLock engineLock(m_engineLock);
+      CLog::Log(LOGDEBUG, "CCoreAudioAE::GarbageCollect - Acquire CA HAL.");
+      Start();
+      m_isSuspended = false;
+    }
+    m_softSuspend = false;
+  }
+  
+  unsigned int curSystemClock = XbmcThreads::SystemClockMillis();
+  if (!m_isSuspended && m_softSuspend && curSystemClock > m_softSuspendTimer)
+  {
+    Suspend();// locks m_engineLock internally
+    CLog::Log(LOGDEBUG, "CCoreAudioAE::GarbageCollect - Release CA HAL.");
+  }
 }
 
 void CCoreAudioAE::EnumerateOutputDevices(AEDeviceList &devices, bool passthrough)
 {
-  if (m_isSuspended)
+  if (m_isSuspended && !m_softSuspend)
     return;
 
   HAL->EnumerateOutputDevices(devices, passthrough);
@@ -660,6 +713,7 @@ void CCoreAudioAE::Stop()
 
 bool CCoreAudioAE::Suspend()
 {
+  CSingleLock engineLock(m_engineLock);
   CLog::Log(LOGDEBUG, "CCoreAudioAE::Suspend - Suspending AE processing");
   m_isSuspended = true;
   // stop all gui sounds
index 5034b68..0803597 100644 (file)
@@ -172,4 +172,7 @@ private:
   int               m_soundMode;
   bool              m_streamsPlaying;
   bool              m_isSuspended;
+  bool              m_softSuspend;
+  unsigned int      m_softSuspendTimer;
+  AudioDeviceID     m_currentAudioDevice;
 };
index 259ce02..54f67d4 100644 (file)
@@ -396,8 +396,7 @@ void CCoreAudioAEHALOSX::EnumerateOutputDevices(AEDeviceList &devices, bool pass
   CoreAudioDeviceList deviceList;
   CCoreAudioHardware::GetOutputDevices(&deviceList);
 
-  std::string defaultDeviceName;
-  CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
+  devices.push_back(AEDevice("Default", "CoreAudio:default"));
 
   std::string deviceName;
   for (int i = 0; !deviceList.empty(); i++)
index be13000..5facfcd 100644 (file)
@@ -361,7 +361,12 @@ bool CCoreAudioDevice::SetHogStatus(bool hog)
       // even if setting hogmode was successfull our PID might not get written
       // into m_HogPid (so it stays -1). Readback hogstatus for judging if we
       // had success on getting hog status
-      m_HogPid = GetHogStatus();
+      // We do this only when AudioObjectSetPropertyData didn't set m_HogPid because
+      // it seems that in the other cases the GetHogStatus could return -1
+      // which would overwrite our valid m_HogPid again
+      // Man we should never touch this shit again ;)
+      if (m_HogPid == -1)
+        m_HogPid = GetHogStatus();
 
       if (ret || m_HogPid != getpid())
       {
index c76b612..9877288 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "CoreAudioAEHAL.h"
 #include "utils/log.h"
+#include "osx/DarwinUtils.h"
 
 bool CCoreAudioHardware::GetAutoHogMode()
 {
@@ -291,22 +292,32 @@ AudioDeviceID CCoreAudioHardware::FindAudioDevice(const std::string &searchName)
 
 AudioDeviceID CCoreAudioHardware::GetDefaultOutputDevice()
 {
+  AudioDeviceID deviceId = 0;
+  static AudioDeviceID lastDeviceId = 0;
+
   AudioObjectPropertyAddress  propertyAddress;
   propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal;
   propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
   propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
-
-  AudioDeviceID deviceId = 0;
+  
   UInt32 size = sizeof(AudioDeviceID);
   OSStatus ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceId);
+
   // outputDevice is set to 0 if there is no audio device available
   // or if the default device is set to an encoded format
   if (ret != noErr || !deviceId) 
   {
     CLog::Log(LOGERROR, "CCoreAudioHardware::GetDefaultOutputDevice:"
       " Unable to identify default output device. Error = %s", GetError(ret).c_str());
-    return 0;
+    // if there was no error and no deviceId was returned
+    // return the last known default device
+    if (ret == noErr && !deviceId)
+      return lastDeviceId;
+    else
+      return 0;
   }
+  
+  lastDeviceId = deviceId;
 
   return deviceId;
 }
@@ -329,9 +340,8 @@ void CCoreAudioHardware::GetOutputDeviceName(std::string& name)
     if (ret != noErr)
       return;
 
-    const char *cstr = CFStringGetCStringPtr(theDeviceName, kCFStringEncodingUTF8);
-    if (cstr)
-      name = cstr;
+    DarwinCFStringRefToString(theDeviceName, name);
+
     CFRelease(theDeviceName);
   }
 }
index 2403bd1..410e20c 100644 (file)
@@ -969,7 +969,6 @@ bool CSoftAE::Suspend()
 {
   CLog::Log(LOGDEBUG, "CSoftAE::Suspend - Suspending AE processing");
   m_isSuspended = true;
-
   CSingleLock streamLock(m_streamLock);
   
   for (StreamList::iterator itt = m_playingStreams.begin(); itt != m_playingStreams.end(); ++itt)
@@ -1021,10 +1020,8 @@ void CSoftAE::Run()
         restart = true;
     }
 
-#if !defined(TARGET_ANDROID)
     /* Handle idle or forced suspend */
     ProcessSuspend();
-#endif
 
     /* if we are told to restart */
     if (m_reOpen || restart || !m_sink)
@@ -1033,6 +1030,17 @@ void CSoftAE::Run()
       InternalOpenSink();
       m_isSuspended = false; // exit Suspend state
     }
+#if defined(TARGET_ANDROID)
+    else if (m_playingStreams.empty() 
+      &&     m_playing_sounds.empty()
+      && !g_advancedSettings.m_streamSilence)
+    {
+      // if we have nothing to do, take a dirt nap.
+      // we do not have to take a lock just to check empty.
+      // this keeps AE from sucking CPU if nothing is going on.
+      m_wake.WaitMSec(SOFTAE_IDLE_WAIT_MSEC);
+    }
+#endif
   }
 }
 
@@ -1388,15 +1396,20 @@ inline void CSoftAE::RemoveStream(StreamList &streams, CSoftAEStream *stream)
 inline void CSoftAE::ProcessSuspend()
 {
   bool sinkIsSuspended = false;
+  unsigned int curSystemClock = 0;
 
-  if (m_playingStreams.empty() && m_playing_sounds.empty() && 
-     !m_softSuspend && !g_advancedSettings.m_streamSilence)
+#if defined(TARGET_WINDOWS)
+  if (!m_softSuspend && m_playingStreams.empty() && m_playing_sounds.empty() &&
+      !g_advancedSettings.m_streamSilence)
   {
     m_softSuspend = true;
     m_softSuspendTimer = XbmcThreads::SystemClockMillis() + 10000; //10.0 second delay for softSuspend
+    Sleep(10);
   }
 
-  unsigned int curSystemClock = XbmcThreads::SystemClockMillis();
+  if (m_softSuspend)
+    curSystemClock = XbmcThreads::SystemClockMillis();
+#endif
 
   /* idle while in Suspend() state until Resume() called */
   /* idle if nothing to play and user hasn't enabled     */
index b885caa..e8a7876 100644 (file)
@@ -228,9 +228,11 @@ bool CAESinkAUDIOTRACK::HasVolume()
   return true;
 }
 
-void  CAESinkAUDIOTRACK::SetVolume(float volume)
+void  CAESinkAUDIOTRACK::SetVolume(float scale)
 {
-  m_volume = volume;
+  // Android uses fixed steps, reverse scale back to percent
+  float gain = CAEUtil::ScaleToGain(scale);
+  m_volume = CAEUtil::GainToPercent(gain);
   m_volume_changed = true;
 }
 
@@ -273,7 +275,6 @@ void CAESinkAUDIOTRACK::Process()
   jmethodID jmRelease           = jenv->GetMethodID(jcAudioTrack, "release", "()V");
   jmethodID jmWrite             = jenv->GetMethodID(jcAudioTrack, "write", "([BII)I");
   jmethodID jmPlayState         = jenv->GetMethodID(jcAudioTrack, "getPlayState", "()I");
-  jmethodID jmSetStereoVolume   = jenv->GetMethodID(jcAudioTrack, "setStereoVolume", "(FF)I");
   jmethodID jmPlayHeadPosition  = jenv->GetMethodID(jcAudioTrack, "getPlaybackHeadPosition", "()I");
   jmethodID jmGetMinBufferSize  = jenv->GetStaticMethodID(jcAudioTrack, "getMinBufferSize", "(III)I");
 
@@ -322,8 +323,7 @@ void CAESinkAUDIOTRACK::Process()
     {
       // check of volume changes and make them,
       // do it here to keep jni calls local to this thread.
-      jfloat jvolume = m_volume;
-      jenv->CallIntMethod(joAudioTrack, jmSetStereoVolume, jvolume, jvolume);
+      CXBMCApp::SetSystemVolume(jenv, m_volume);
       m_volume_changed = false;
     }
     if (m_draining)
index e7ac7fc..46b3551 100644 (file)
@@ -42,7 +42,7 @@ public:
   virtual unsigned int AddPackets      (uint8_t *data, unsigned int frames, bool hasAudio);
   virtual void         Drain           ();
   virtual bool         HasVolume       ();
-  virtual void         SetVolume       (float volume);
+  virtual void         SetVolume       (float scale);
   static void          EnumerateDevicesEx(AEDeviceInfoList &list);
 
 private:
index be52f63..3208a05 100644 (file)
@@ -71,6 +71,19 @@ public:
     return (value - 1)*db_range;
   }
 
+  /*! \brief convert a dB gain to volume percentage (as a proportion)
+   We assume a dB range of 60dB, i.e. assume that 0% volume corresponds
+   to a reduction of 60dB.
+   \param the corresponding gain in dB from -60dB .. 0dB.
+   \return value the volume from 0..1
+   \sa ScaleToGain
+   */
+  static inline const float GainToPercent(const float gain)
+  {
+    static const float db_range = 60.0f;
+    return 1+(gain/db_range);
+  }
+
   /*! \brief convert a dB gain to a scale factor for audio manipulation
    Inverts gain = 20 log_10(scale)
    \param dB the gain in decibels.
@@ -82,6 +95,17 @@ public:
     return pow(10.0f, dB/20);
   }
 
+  /*! \brief convert a scale factor to dB gain for audio manipulation
+   Inverts GainToScale result
+   \param the scale factor (equivalent to a voltage multiplier).
+   \return dB the gain in decibels.
+   \sa GainToScale
+   */
+  static inline const float ScaleToGain(const float scale)
+  {
+    return 20*log10(scale);
+  }
+
   #ifdef __SSE__
   static void SSEMulArray     (float *data, const float mul, uint32_t count);
   static void SSEMulAddArray  (float *data, float *add, const float mul, uint32_t count);
index 2042026..b9f8740 100644 (file)
@@ -114,7 +114,9 @@ public:
   virtual void SeekPercentage(float fPercent = 0){}
   virtual float GetPercentage(){ return 0;}
   virtual float GetCachePercentage(){ return 0;}
+  virtual void SetMute(bool bOnOff){}
   virtual void SetVolume(float volume){}
+  virtual bool ControlsVolume(){ return false;}
   virtual void SetDynamicRangeCompression(long drc){}
   virtual void GetAudioInfo( CStdString& strAudioInfo) = 0;
   virtual void GetVideoInfo( CStdString& strVideoInfo) = 0;
index 6860233..a54ff31 100644 (file)
@@ -106,9 +106,9 @@ bool CBaseRenderer::FindResolutionFromOverride(float fps, float& weight, bool fa
 
     for (size_t j = (int)RES_DESKTOP; j < g_settings.m_ResInfo.size(); j++)
     {
-      if (g_settings.m_ResInfo[j].iWidth  == g_settings.m_ResInfo[m_resolution].iWidth
-       && g_settings.m_ResInfo[j].iHeight == g_settings.m_ResInfo[m_resolution].iHeight
-       && g_settings.m_ResInfo[j].iScreen == g_settings.m_ResInfo[m_resolution].iScreen)
+      if (g_settings.m_ResInfo[j].iScreenWidth  == g_settings.m_ResInfo[m_resolution].iScreenWidth
+       && g_settings.m_ResInfo[j].iScreenHeight == g_settings.m_ResInfo[m_resolution].iScreenHeight
+       && g_settings.m_ResInfo[j].iScreen       == g_settings.m_ResInfo[m_resolution].iScreen)
       {
         if (g_settings.m_ResInfo[j].fRefreshRate <= override.refreshmax
          && g_settings.m_ResInfo[j].fRefreshRate >= override.refreshmin)
@@ -161,9 +161,9 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight)
       for (size_t i = (int)RES_DESKTOP; i < g_settings.m_ResInfo.size(); i++)
       {
         if (MathUtils::round_int(g_settings.m_ResInfo[i].fRefreshRate) == 60
-         && g_settings.m_ResInfo[i].iWidth  == g_settings.m_ResInfo[m_resolution].iWidth
-         && g_settings.m_ResInfo[i].iHeight == g_settings.m_ResInfo[m_resolution].iHeight
-         && g_settings.m_ResInfo[i].iScreen == g_settings.m_ResInfo[m_resolution].iScreen)
+         && g_settings.m_ResInfo[i].iScreenWidth  == g_settings.m_ResInfo[m_resolution].iScreenWidth
+         && g_settings.m_ResInfo[i].iScreenHeight == g_settings.m_ResInfo[m_resolution].iScreenHeight
+         && g_settings.m_ResInfo[i].iScreen       == g_settings.m_ResInfo[m_resolution].iScreen)
         {
           if (fabs(g_settings.m_ResInfo[i].fRefreshRate - 60.0) < fabs(g_settings.m_ResInfo[m_resolution].fRefreshRate - 60.0))
             m_resolution = (RESOLUTION)i;
@@ -176,10 +176,10 @@ void CBaseRenderer::FindResolutionFromFpsMatch(float fps, float& weight)
         CLog::Log(LOGDEBUG, "60 hertz refreshrate not available, choosing highest");
         for (size_t i = (int)RES_DESKTOP; i < g_settings.m_ResInfo.size(); i++)
         {
-          if (g_settings.m_ResInfo[i].fRefreshRate >  g_settings.m_ResInfo[m_resolution].fRefreshRate
-           && g_settings.m_ResInfo[i].iWidth       == g_settings.m_ResInfo[m_resolution].iWidth
-           && g_settings.m_ResInfo[i].iHeight      == g_settings.m_ResInfo[m_resolution].iHeight
-           && g_settings.m_ResInfo[i].iScreen      == g_settings.m_ResInfo[m_resolution].iScreen)
+          if (g_settings.m_ResInfo[i].fRefreshRate  >  g_settings.m_ResInfo[m_resolution].fRefreshRate
+           && g_settings.m_ResInfo[i].iScreenWidth  == g_settings.m_ResInfo[m_resolution].iScreenWidth
+           && g_settings.m_ResInfo[i].iScreenHeight == g_settings.m_ResInfo[m_resolution].iScreenHeight
+           && g_settings.m_ResInfo[i].iScreen       == g_settings.m_ResInfo[m_resolution].iScreen)
           {
             m_resolution = (RESOLUTION)i;
           }
@@ -195,15 +195,13 @@ RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RES
 {
   RESOLUTION_INFO &curr = g_settings.m_ResInfo[current];
 
-  int iWidth  = curr.iWidth;
-  int iHeight = curr.iHeight;
+  int iScreenWidth  = curr.iScreenWidth;
+  int iScreenHeight = curr.iScreenHeight;
   float fRefreshRate = fps;
 
   /*
    * For 3D modes the following is assumed :
    *
-   * fps is fps * 2 : 25 fps -> 50 fps
-   *
    * side-by-side :
    *
    * width is width / 2 : 1920 -> 960
@@ -214,15 +212,23 @@ RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RES
    *
    */
 
+  // work out current non-3D resolution (in case we are currently in 3D mode)
+  if (curr.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
+  {
+    iScreenWidth *= 2;
+  }
+  else if (curr.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+  {
+    iScreenHeight *= 2;
+  }
+  // work out resolution if we switch to 3D mode
   if(m_iFlags & CONF_FLAGS_FORMAT_SBS)
   {
-    iWidth /= 2;
-    fRefreshRate *= 2;
+    iScreenWidth /= 2;
   }
   else if(m_iFlags & CONF_FLAGS_FORMAT_TB)
   {
-    iHeight /= 2;
-    fRefreshRate *= 2;
+    iScreenHeight /= 2;
   }
 
   float last_diff = fRefreshRate;
@@ -235,8 +241,8 @@ RESOLUTION CBaseRenderer::FindClosestResolution(float fps, float multiplier, RES
 
     //discard resolutions that are not the same width and height
     //or have a too low refreshrate
-    if (info.iWidth  != iWidth
-    ||  info.iHeight != iHeight
+    if (info.iScreenWidth  != iScreenWidth
+    ||  info.iScreenHeight != iScreenHeight
     ||  info.iScreen != curr.iScreen
     ||  info.fRefreshRate < (fRefreshRate * multiplier / 1.001) - 0.001)
       continue;
@@ -500,11 +506,6 @@ void CBaseRenderer::CalcNormalDisplayRect(float offsetX, float offsetY, float sc
 //***************************************************************************************
 void CBaseRenderer::CalculateFrameAspectRatio(unsigned int desired_width, unsigned int desired_height)
 {
-  if(m_iFlags & CONF_FLAGS_FORMAT_SBS)
-    desired_width /= 2;
-  else if(m_iFlags & CONF_FLAGS_FORMAT_TB)
-    desired_height /= 2;
-
   m_sourceFrameRatio = (float)desired_width / desired_height;
 
   // Check whether mplayer has decided that the size of the video file should be changed
@@ -577,6 +578,9 @@ void CBaseRenderer::SetViewMode(int viewMode)
   if (viewMode < VIEW_MODE_NORMAL || viewMode > VIEW_MODE_CUSTOM)
     viewMode = VIEW_MODE_NORMAL;
 
+  if (m_iFlags & (CONF_FLAGS_FORMAT_SBS | CONF_FLAGS_FORMAT_TB))
+    viewMode = VIEW_MODE_NORMAL;
+
   g_settings.m_currentVideoSettings.m_ViewMode = viewMode;
 
   // get our calibrated full screen resolution
index 13014e2..ebf8f1c 100644 (file)
@@ -777,14 +777,36 @@ float CAMLPlayer::GetPercentage()
     return 0.0f;
 }
 
-void CAMLPlayer::SetVolume(float volume)
+void CAMLPlayer::SetMute(bool bOnOff)
 {
-  CLog::Log(LOGDEBUG, "CAMLPlayer::SetVolume(%f)", volume);
+  m_audio_mute = bOnOff;
 #if !defined(TARGET_ANDROID)
   CSingleLock lock(m_aml_csection);
-  // volume is a float percent from 0.0 to 1.0
   if (m_dll->check_pid_valid(m_pid))
-    m_dll->audio_set_volume(m_pid, volume);
+  {
+    if (m_audio_mute)
+      m_dll->audio_set_volume(m_pid, 0.0);
+    else
+      m_dll->audio_set_volume(m_pid, m_audio_volume);
+  }
+#endif
+}
+
+void CAMLPlayer::SetVolume(float volume)
+{
+  m_audio_volume = 0.0f;
+  if (volume > VOLUME_MINIMUM)
+  {
+    float dB = CAEUtil::PercentToGain(volume);
+    m_audio_volume = CAEUtil::GainToScale(dB);
+  }
+  if (m_audio_volume >= 0.99f)
+    m_audio_volume = 1.0f;
+
+#if !defined(TARGET_ANDROID)
+  CSingleLock lock(m_aml_csection);
+  if (!m_audio_mute && m_dll->check_pid_valid(m_pid))
+    m_dll->audio_set_volume(m_pid, m_audio_volume);
 #endif
 }
 
@@ -1090,6 +1112,8 @@ void CAMLPlayer::SeekTime(__int64 seek_ms)
     m_dll->player_timesearch(m_pid, (float)seek_ms/1000.0);
     WaitForSearchOK(5000);
     WaitForPlaying(5000);
+    // restore system volume setting.
+    SetVolume(m_audio_volume);
   }
 }
 
@@ -1440,6 +1464,9 @@ void CAMLPlayer::Process()
       // get our initial status.
       GetStatus();
 
+      // restore mute setting.
+      SetMute(g_settings.m_bMute);
+
       // restore system volume setting.
       SetVolume(g_settings.m_fVolumeLevel);
 
@@ -1815,8 +1842,15 @@ bool CAMLPlayer::WaitForSearchOK(int timeout_ms)
 
 bool CAMLPlayer::WaitForPlaying(int timeout_ms)
 {
+  // force the volume off in case we are starting muted
+  m_audio_mute = true;
   while (!m_bAbortRequest && (timeout_ms > 0))
   {
+#if !defined(TARGET_ANDROID)
+    // anoying that we have to hammer audio_set_volume
+    // but have to catch it before any audio comes out.
+    m_dll->audio_set_volume(m_pid, 0.0);
+#endif
     player_status pstatus = (player_status)GetPlayerSerializedState();
     if (m_log_level > 5)
       CLog::Log(LOGDEBUG, "CAMLPlayer::WaitForPlaying: %s", m_dll->player_status2str(pstatus));
index 54d8fae..a90323c 100644 (file)
@@ -80,7 +80,9 @@ public:
   virtual bool  SeekScene(bool bPlus = true);
   virtual void  SeekPercentage(float fPercent = 0.0f);
   virtual float GetPercentage();
+  virtual void  SetMute(bool bOnOff);
   virtual void  SetVolume(float volume);
+  virtual bool  ControlsVolume() {return true;}
   virtual void  SetDynamicRangeCompression(long drc)              {}
   virtual void  GetAudioInfo(CStdString &strAudioInfo);
   virtual void  GetVideoInfo(CStdString &strVideoInfo);
@@ -223,6 +225,8 @@ private:
   int                     m_audio_delay;
   bool                    m_audio_passthrough_ac3;
   bool                    m_audio_passthrough_dts;
+  bool                    m_audio_mute;
+  float                   m_audio_volume;
 
   int                     m_video_index;
   int                     m_video_count;
index 1d8bad3..104cbbf 100644 (file)
@@ -192,6 +192,12 @@ public:
   virtual void SetDropState(bool bDrop) = 0;
 
   /*
+   * will be called by video player indicating the playback speed. see DVD_PLAYSPEED_NORMAL,
+   * DVD_PLAYSPEED_PAUSE and friends.
+   */
+  virtual void SetSpeed(int iSpeed) {};
+
+  /*
    * returns the number of demuxer bytes in any internal buffers
    */
   virtual int GetDataSize(void)
index bc7000e..d2c7b91 100644 (file)
@@ -804,6 +804,7 @@ bool CDVDVideoCodecVDA::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
     }
  
     m_use_cvBufferRef = true;
+#if 0
     //TODO fix after Frodo if (g_Windowing.GetRenderVendor().Find("Intel") > -1)
     {
       m_dllSwScale = new DllSwScale;
@@ -847,6 +848,7 @@ bool CDVDVideoCodecVDA::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options)
 
       m_use_cvBufferRef = false;
     }
+#endif
 
     // setup the decoder configuration dict
     CFMutableDictionaryRef decoderConfiguration = CFDictionaryCreateMutable(
index 3008c25..bc79b92 100644 (file)
@@ -465,6 +465,8 @@ void CDVDPlayerVideo::Process()
       m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
       if(m_speed == DVD_PLAYSPEED_PAUSE)
         m_iNrOfPicturesNotToSkip = 0;
+      if (m_pVideoCodec)
+        m_pVideoCodec->SetSpeed(m_speed);
     }
     else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED))
     {
index 098bbf7..a07164b 100644 (file)
@@ -112,7 +112,10 @@ bool CEdl::ReadEditDecisionLists(const CStdString& strMovie, const float fFrameR
    * Only check for edit decision lists if the movie is on the local hard drive, or accessed over a
    * network share.
    */
-  if ((URIUtils::IsHD(strMovie) ||  URIUtils::IsSmb(strMovie)) &&
+  if ((URIUtils::IsHD(strMovie)  || 
+       URIUtils::IsSmb(strMovie) || 
+       URIUtils::IsNfs(strMovie) || 
+       URIUtils::IsAfp(strMovie))         &&
       !URIUtils::IsPVRRecording(strMovie) &&
       !URIUtils::IsInternetStream(strMovie))
   {
index 5399b10..10c3329 100644 (file)
 #include "guilib/LocalizeStrings.h"
 #include "cores/AudioEngine/Utils/AEConvert.h"
 
-#ifndef VOLUME_MINIMUM
-#define VOLUME_MINIMUM -6000  // -60dB
-#endif
-
 using namespace std;
 
 #define OMX_MAX_CHANNELS 10
@@ -77,6 +73,19 @@ static const uint16_t AC3FSCod   [] = {48000, 44100, 32000, 0};
 
 static const uint16_t DTSFSCod   [] = {0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, 12000, 24000, 48000, 0, 0};
 
+// 7.1 downmixing coefficients
+const float downmixing_coefficients_8[OMX_AUDIO_MAXCHANNELS] = {
+  //        L       R
+  /* L */   1,      0,
+  /* R */   0,      1,
+  /* C */   0.7071, 0.7071,
+  /* LFE */ 0.7071, 0.7071,
+  /* Ls */  0.7071, 0,
+  /* Rs */  0,      0.7071,
+  /* Lr */  0.7071, 0,
+  /* Rr */  0,      0.7071
+};
+
 //////////////////////////////////////////////////////////////////////
 // Construction/Destruction
 //////////////////////////////////////////////////////////////////////
@@ -198,8 +207,6 @@ bool COMXAudio::Initialize(AEAudioFormat format, std::string& device, OMXClock *
 
   m_drc         = 0;
 
-  m_CurrentVolume = g_settings.m_fVolumeLevel; 
-
   memset(m_input_channels, 0x0, sizeof(m_input_channels));
   memset(m_output_channels, 0x0, sizeof(m_output_channels));
   memset(&m_wave_header, 0x0, sizeof(m_wave_header));
@@ -545,8 +552,6 @@ bool COMXAudio::Initialize(AEAudioFormat format, std::string& device, OMXClock *
   m_first_frame   = true;
   m_last_pts      = DVD_NOPTS_VALUE;
 
-  SetCurrentVolume(m_CurrentVolume);
-
   CLog::Log(LOGDEBUG, "COMXAudio::Initialize Ouput bps %d samplerate %d channels %d buffer size %d bytes per second %d", 
       (int)m_pcm_output.nBitPerSample, (int)m_pcm_output.nSamplingRate, (int)m_pcm_output.nChannels, m_BufferLen, m_BytesPerSec);
   CLog::Log(LOGDEBUG, "COMXAudio::Initialize Input bps %d samplerate %d channels %d buffer size %d bytes per second %d", 
@@ -694,20 +699,76 @@ bool COMXAudio::SetCurrentVolume(float fVolume)
   CSingleLock lock (m_critSection);
 
   if(!m_Initialized || m_Passthrough)
-    return -1;
-
+    return false;
+  double gain = pow(10, (g_advancedSettings.m_ac3Gain - 12.0f) / 20.0);
   m_CurrentVolume = fVolume;
 
-  OMX_AUDIO_CONFIG_VOLUMETYPE volume;
-  OMX_INIT_STRUCTURE(volume);
-  volume.nPortIndex = m_omx_render->GetInputPort();
+  if (m_format.m_channelLayout.Count() > 2)
+  {
+    double r = fVolume;
+    const float* coeff = downmixing_coefficients_8;
+    int input_channels = 0;
+
+    // normally we normalalise the levels, can be skipped (boosted) at risk of distortion
+    if(!g_guiSettings.GetBool("audiooutput.normalizelevels"))
+    {
+      double sum_L = 0;
+      double sum_R = 0;
+
+      for(size_t i = 0; i < OMX_AUDIO_MAXCHANNELS; ++i)
+      {
+        if (m_input_channels[i] == OMX_AUDIO_ChannelMax)
+          break;
+        if(i & 1)
+          sum_R += coeff[i];
+        else
+          sum_L += coeff[i];
+      }
+
+      r /= max(sum_L, sum_R);
+    }
+
+    // the analogue volume is too quiet for some. Allow use of an advancedsetting to boost this (at risk of distortion)
+    r *= gain;
 
-  volume.bLinear    = OMX_TRUE;
-  float hardwareVolume = std::max(VOLUME_MINIMUM, std::min(VOLUME_MAXIMUM, fVolume)) * 100.0f;
-  volume.sVolume.nValue = (int)hardwareVolume;
+    OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS mix;
+    OMX_INIT_STRUCTURE(mix);
+    mix.nPortIndex = m_omx_mixer.GetInputPort();
 
-  m_omx_render->SetConfig(OMX_IndexConfigAudioVolume, &volume);
+    assert(sizeof(mix.coeff)/sizeof(mix.coeff[0]) == 16);
 
+    for(size_t i = 0; i < 16; ++i)
+      mix.coeff[i] = static_cast<unsigned int>(0x10000 * (coeff[i] * r));
+
+    OMX_ERRORTYPE omx_err =
+      m_omx_mixer.SetConfig(OMX_IndexConfigBrcmAudioDownmixCoefficients, &mix);
+
+    if(omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "%s::%s - error setting OMX_IndexConfigBrcmAudioDownmixCoefficients, error 0x%08x\n",
+                CLASSNAME, __func__, omx_err);
+      return false;
+    }
+  }
+  else
+  {
+    OMX_AUDIO_CONFIG_VOLUMETYPE volume;
+    OMX_INIT_STRUCTURE(volume);
+    volume.nPortIndex = m_omx_render->GetInputPort();
+
+    volume.bLinear    = OMX_TRUE;
+    float hardwareVolume = fVolume * gain * 100.0f;
+    volume.sVolume.nValue = (int)(hardwareVolume + 0.5f);
+
+    OMX_ERRORTYPE omx_err =
+      m_omx_render->SetConfig(OMX_IndexConfigAudioVolume, &volume);
+    if(omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "%s::%s - error setting OMX_IndexConfigAudioVolume, error 0x%08x\n",
+                CLASSNAME, __func__, omx_err);
+      return false;
+    }
+  }  
   return true;
 }
 
@@ -805,6 +866,8 @@ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dt
     if(m_av_clock->AudioStart())
     {
       omx_buffer->nFlags = OMX_BUFFERFLAG_STARTTIME;
+      if(pts == DVD_NOPTS_VALUE)
+        omx_buffer->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN;
 
       m_last_pts = pts;
 
@@ -885,16 +948,6 @@ unsigned int COMXAudio::AddPackets(const void* data, unsigned int len, double dt
           }
         }
 
-        if ((m_pcm_input.nChannels > m_pcm_output.nChannels) &&g_guiSettings.GetBool("audiooutput.normalizelevels"))
-        {
-          OMX_AUDIO_CONFIG_VOLUMETYPE volume;
-          OMX_INIT_STRUCTURE(volume);
-          volume.nPortIndex = m_omx_mixer.GetInputPort();
-          volume.bLinear    = OMX_FALSE;
-          volume.sVolume.nValue = (int)(g_advancedSettings.m_ac3Gain*100.0f+0.5f);
-          m_omx_mixer.SetConfig(OMX_IndexConfigAudioVolume, &volume);
-        }
-
         memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels));
         m_pcm_input.nSamplingRate = m_format.m_sampleRate;
 
index aac591c..60aa9ab 100644 (file)
@@ -460,6 +460,7 @@ bool COMXPlayer::OpenFile(const CFileItem &file, const CPlayerOptions &options)
     m_UpdateApplication = 0;
     m_offset_pts        = 0;
     m_current_volume    = 0;
+    m_current_mute      = false;
     m_change_volume     = true;
 
     m_item              = file;
@@ -1262,7 +1263,7 @@ void COMXPlayer::Process()
 
     if(m_change_volume)
     {
-      m_player_audio.SetCurrentVolume(m_current_volume);
+      m_player_audio.SetCurrentVolume(m_current_mute ? VOLUME_MINIMUM : m_current_volume);
       m_change_volume = false;
     }
 
@@ -2957,8 +2958,10 @@ bool COMXPlayer::OpenVideoStream(int iStream, int source, bool reset)
     m_player_video.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_RESET));
 
   unsigned flags = 0;
-  if(m_filename.find("3DSBS") != string::npos
+  if(m_filename.find("3DSBS") != string::npos || m_filename.find("HSBS") != string::npos)
     flags = CONF_FLAGS_FORMAT_SBS;
+  else if(m_filename.find("3DTAB") != string::npos || m_filename.find("HTAB") != string::npos)
+    flags = CONF_FLAGS_FORMAT_TB;
   m_player_video.SetFlags(flags);
 
   /* store information about stream */
@@ -4134,6 +4137,12 @@ void COMXPlayer::GetVideoRect(CRect& SrcRect, CRect& DestRect)
   g_renderManager.GetVideoRect(SrcRect, DestRect);
 }
 
+void COMXPlayer::SetMute(bool bOnOff)
+{
+  m_current_mute = bOnOff;
+  m_change_volume = true;
+}
+
 void COMXPlayer::SetVolume(float fVolume)
 {
   m_current_volume = fVolume;
index 3a2a63d..ca824c2 100644 (file)
@@ -227,7 +227,9 @@ public:
   virtual float GetPercentage();
   virtual float GetCachePercentage();
 
+  virtual void  SetMute(bool bOnOff);
   virtual void  SetVolume(float fVolume);
+  virtual bool  ControlsVolume() {return true;}
   virtual void  SetDynamicRangeCompression(long drc)              {}
   virtual void  GetAudioInfo(CStdString &strAudioInfo);
   virtual void  GetVideoInfo(CStdString &strVideoInfo);
@@ -484,6 +486,7 @@ private:
   CEvent                  m_ready;
 
   float                   m_current_volume;
+  bool                    m_current_mute;
   bool                    m_change_volume;
   CDVDOverlayContainer    m_overlayContainer;
   ECacheState             m_caching;
index 7b0a5d2..90f94aa 100644 (file)
@@ -99,8 +99,8 @@ OMXPlayerVideo::OMXPlayerVideo(OMXClock *av_clock,
   m_messageQueue.SetMaxTimeSize(8.0);
 
   RESOLUTION res  = g_graphicsContext.GetVideoResolution();
-  m_video_width   = g_settings.m_ResInfo[res].iWidth;
-  m_video_height  = g_settings.m_ResInfo[res].iHeight;
+  m_video_width   = g_settings.m_ResInfo[res].iScreenWidth;
+  m_video_height  = g_settings.m_ResInfo[res].iScreenHeight;
 
   m_dst_rect.SetRect(0, 0, 0, 0);
 
@@ -337,16 +337,25 @@ void OMXPlayerVideo::Output(int iGroupId, double pts, bool bDropPacket)
         flags |= CONF_FLAGS_FORMAT_SBS;
       }
     }
-
-    CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f. format: BYPASS",
-        __FUNCTION__, m_width, m_height, m_fps);
+    else if(m_flags & CONF_FLAGS_FORMAT_TB)
+    {
+      if(g_Windowing.Support3D(m_video_width, m_video_height, D3DPRESENTFLAG_MODE3DTB))
+      {
+        CLog::Log(LOGNOTICE, "3DTB movie found");
+        flags |= CONF_FLAGS_FORMAT_TB;
+      }
+    }
 
     unsigned int iDisplayWidth  = m_hints.width;
     unsigned int iDisplayHeight = m_hints.height;
+
     /* use forced aspect if any */
     if( m_fForcedAspectRatio != 0.0f )
       iDisplayWidth = (int) (iDisplayHeight * m_fForcedAspectRatio);
 
+    CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f. %dx%x format: BYPASS",
+        __FUNCTION__, m_width, m_height, m_fps, iDisplayWidth, iDisplayHeight);
+
     if(!g_renderManager.Configure(m_hints.width, m_hints.height,
           iDisplayWidth, iDisplayHeight, m_fps, flags, format, 0,
           m_hints.orientation))
@@ -813,21 +822,22 @@ void OMXPlayerVideo::SetVideoRect(const CRect &SrcRect, const CRect &DestRect)
   CRect gui, display, dst_rect;
   RESOLUTION res = g_graphicsContext.GetVideoResolution();
   gui.SetRect(0, 0, g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight);
-  display.SetRect(0, 0, g_settings.m_ResInfo[res].iWidth, g_settings.m_ResInfo[res].iHeight);
+  display.SetRect(0, 0, g_settings.m_ResInfo[res].iScreenWidth, g_settings.m_ResInfo[res].iScreenHeight);
   
   dst_rect = m_dst_rect;
   if (gui != display)
   {
     float xscale = display.Width()  / gui.Width();
     float yscale = display.Height() / gui.Height();
+    // video is displayed in absolute coordinates (bypassing half width or height GUI mode)
+    if (m_flags & CONF_FLAGS_FORMAT_SBS) xscale *= 2.0f;
+    if (m_flags & CONF_FLAGS_FORMAT_TB)  yscale *= 2.0f;
     dst_rect.x1 *= xscale;
     dst_rect.x2 *= xscale;
     dst_rect.y1 *= yscale;
     dst_rect.y2 *= yscale;
   }
-
-  if(!(m_flags & CONF_FLAGS_FORMAT_SBS) && !(m_flags & CONF_FLAGS_FORMAT_TB))
-    m_omxVideo.SetVideoRect(SrcRect, m_dst_rect);
+  m_omxVideo.SetVideoRect(SrcRect, dst_rect);
 }
 
 void OMXPlayerVideo::RenderUpdateCallBack(const void *ctx, const CRect &SrcRect, const CRect &DestRect)
index 77d7af1..9215fd9 100644 (file)
@@ -436,6 +436,23 @@ bool COMXVideo::Open(CDVDStreamInfo &hints, OMXClock *clock, bool deinterlace, b
     }
   }
 
+  // broadcom omx entension:
+  // When enabled, the timestamp fifo mode will change the way incoming timestamps are associated with output images.
+  // In this mode the incoming timestamps get used without re-ordering on output images.
+  if(hints.ptsinvalid)
+  {
+    OMX_CONFIG_BOOLEANTYPE timeStampMode;
+    OMX_INIT_STRUCTURE(timeStampMode);
+    timeStampMode.bEnabled = OMX_TRUE;
+
+    omx_err = m_omx_decoder.SetParameter((OMX_INDEXTYPE)OMX_IndexParamBrcmVideoTimestampFifo, &timeStampMode);
+    if (omx_err != OMX_ErrorNone)
+    {
+      CLog::Log(LOGERROR, "COMXVideo::Open OMX_IndexParamBrcmVideoTimestampFifo error (0%08x)\n", omx_err);
+      return false;
+    }
+  }
+
   if(NaluFormatStartCodes(hints.codec, m_extradata, m_extrasize))
   {
     OMX_NALSTREAMFORMATTYPE nalStreamFormat;
@@ -648,6 +665,9 @@ bool COMXVideo::Open(CDVDStreamInfo &hints, OMXClock *clock, bool deinterlace, b
     m_deinterlace, m_hdmi_clock_sync);
 
   m_first_frame   = true;
+  // start from assuming all recent frames had valid pts
+  m_history_valid_pts = ~0;
+
   return true;
 }
 
@@ -702,6 +722,14 @@ unsigned int COMXVideo::GetSize()
   return m_omx_decoder.GetInputBufferSize();
 }
 
+static unsigned count_bits(int32_t value)
+{
+  unsigned bits = 0;
+  for(;value;++bits)
+    value &= value - 1;
+  return bits;
+}
+
 int COMXVideo::Decode(uint8_t *pData, int iSize, double dts, double pts)
 {
   OMX_ERRORTYPE omx_err;
@@ -736,29 +764,31 @@ int COMXVideo::Decode(uint8_t *pData, int iSize, double dts, double pts)
 
       omx_buffer->nFlags = 0;
       omx_buffer->nOffset = 0;
-
-      uint64_t val  = (uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts;
+      // some packed bitstream AVI files set almost all pts values to DVD_NOPTS_VALUE, but have a scattering of real pts values.
+      // the valid pts values match the dts values.
+      // if a stream has had more than 4 valid pts values in the last 16, the use UNKNOWN, otherwise use dts
+      m_history_valid_pts = (m_history_valid_pts << 1) | (pts != DVD_NOPTS_VALUE);
+      if(pts == DVD_NOPTS_VALUE && count_bits(m_history_valid_pts & 0xffff) < 4)
+        pts = dts;
 
       if(m_av_clock->VideoStart())
       {
-        // only send dts on first frame to get nerly correct starttime
+        // only send dts on first frame to get nearly correct starttime
         if(pts == DVD_NOPTS_VALUE)
           pts = dts;
-        val  = (uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts;
-
+        if(pts == DVD_NOPTS_VALUE)
+          omx_buffer->nFlags |= OMX_BUFFERFLAG_TIME_UNKNOWN;
         omx_buffer->nFlags = OMX_BUFFERFLAG_STARTTIME;
-        CLog::Log(LOGDEBUG, "OMXVideo::Decode VDec : setStartTime %f\n", (float)val / DVD_TIME_BASE);
+        CLog::Log(LOGDEBUG, "OMXVideo::Decode VDec : setStartTime %f\n", (pts == DVD_NOPTS_VALUE ? 0.0 : pts) / DVD_TIME_BASE);
         m_av_clock->VideoStart(false);
-        omx_buffer->nTimeStamp = ToOMXTime(val);
       }
       else
       {
         if(pts == DVD_NOPTS_VALUE)
           omx_buffer->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;
-        omx_buffer->nTimeStamp = ToOMXTime(val);
       }
 
-
+      omx_buffer->nTimeStamp = ToOMXTime((uint64_t)(pts == DVD_NOPTS_VALUE) ? 0 : pts);
       omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes;
       memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen);
 
@@ -910,14 +940,8 @@ void COMXVideo::SetVideoRect(const CRect& SrcRect, const CRect& DestRect)
     return;
 
   OMX_CONFIG_DISPLAYREGIONTYPE configDisplay;
-  OMX_INIT_STRUCTURE(configDisplay);
-  configDisplay.nPortIndex = m_omx_render.GetInputPort();
-  RESOLUTION res = g_graphicsContext.GetVideoResolution();
-  // DestRect is in GUI coordinates, rather than display coordinates, so we have to scale
-  float xscale = (float)g_settings.m_ResInfo[res].iScreenWidth  / (float)g_settings.m_ResInfo[res].iWidth;
-  float yscale = (float)g_settings.m_ResInfo[res].iScreenHeight / (float)g_settings.m_ResInfo[res].iHeight;
   float sx1 = SrcRect.x1, sy1 = SrcRect.y1, sx2 = SrcRect.x2, sy2 = SrcRect.y2;
-  float dx1 = DestRect.x1*xscale, dy1 = DestRect.y1*yscale, dx2 = DestRect.x2*xscale, dy2 = DestRect.y2*yscale;
+  float dx1 = DestRect.x1, dy1 = DestRect.y1, dx2 = DestRect.x2, dy2 = DestRect.y2;
   float sw = SrcRect.Width() / DestRect.Width();
   float sh = SrcRect.Height() / DestRect.Height();
 
@@ -931,6 +955,8 @@ void COMXVideo::SetVideoRect(const CRect& SrcRect, const CRect& DestRect)
     dy1 -= dy1;
   }
 
+  OMX_INIT_STRUCTURE(configDisplay);
+  configDisplay.nPortIndex = m_omx_render.GetInputPort();
   configDisplay.fullscreen = OMX_FALSE;
   configDisplay.noaspect   = OMX_TRUE;
 
index f58187f..180c2db 100644 (file)
@@ -90,6 +90,7 @@ protected:
   bool              m_deinterlace;
   bool              m_hdmi_clock_sync;
   bool              m_first_frame;
+  uint32_t          m_history_valid_pts;
 
   bool NaluFormatStartCodes(enum CodecID codec, uint8_t *in_extradata, int in_extrasize);
 };
index c04da03..b3974f4 100644 (file)
@@ -766,6 +766,9 @@ int CGUIDialogContextMenu::ShowAndGetChoice(const CContextButtons &choices)
   CGUIDialogContextMenu *pMenu = (CGUIDialogContextMenu *)g_windowManager.GetWindow(WINDOW_DIALOG_CONTEXT_MENU);
   if (pMenu)
   {
+    if (pMenu->IsDialogRunning())
+      return -1;
+
     pMenu->m_buttons = choices;
     pMenu->Initialize();
     pMenu->SetInitialVisibility();
index 4bea584..32b7388 100644 (file)
@@ -321,7 +321,7 @@ void CGUIEPGGridContainer::Render()
     {
       /* first program starts before current view */
       int startBlock = blockOffset - 1;
-      while (m_gridIndex[channel][startBlock].item == item)
+      while (startBlock >= 0 && m_gridIndex[channel][startBlock].item == item)
         startBlock--;
 
       block = startBlock + 1;
index f96bdc3..00bf705 100644 (file)
@@ -26,6 +26,7 @@
 #include "utils/URIUtils.h"
 #include "FileItem.h"
 #include "test/TestUtils.h"
+#include "utils/StringUtils.h"
 
 #include <errno.h>
 
@@ -188,4 +189,452 @@ TEST(TestRarFile, CorruptedFile)
   file->Close();
   XBMC_DELETETEMPFILE(file);
 }
+
+TEST(TestRarFile, StoredRAR)
+{
+  XFILE::CFile file;
+  char buf[20];
+  memset(&buf, 0, sizeof(buf));
+  CStdString reffile, strrarpath, strpathinrar;
+  CFileItemList itemlist, itemlistemptydir;
+  struct __stat64 stat_buffer;
+
+  reffile = XBMC_REF_FILE_PATH("xbmc/filesystem/test/refRARstored.rar");
+  URIUtils::CreateArchivePath(strrarpath, "rar", reffile, "");
+  ASSERT_TRUE(XFILE::CDirectory::GetDirectory(strrarpath, itemlist));
+  itemlist.Sort(SORT_METHOD_FULLPATH, SortOrderAscending);
+
+  /* /reffile.txt */
+  /*
+   * NOTE: Use of Seek gives inconsistent behavior from when seeking through
+   * an uncompressed RAR archive. See TestRarFile.Read test case.
+   */
+  strpathinrar = itemlist[1]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/reffile.txt", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFREG);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(0, file.GetPosition());
+  EXPECT_EQ(1616, file.GetLength());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_TRUE(file.ReadString(buf, sizeof(buf)));
+  EXPECT_EQ(39, file.GetPosition());
+  EXPECT_STREQ("an award-winning fr", buf);
+  EXPECT_EQ(100, file.Seek(100));
+  EXPECT_EQ(100, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(120, file.GetPosition());
+  EXPECT_TRUE(!memcmp("ent hub for digital ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(220, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(220, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(240, file.GetPosition());
+  EXPECT_TRUE(!memcmp("rs, XBMC is a non-pr", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1596, file.Seek(-(int64_t)sizeof(buf), SEEK_END));
+  EXPECT_EQ(1596, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_TRUE(!memcmp("multimedia jukebox.\n", buf, sizeof(buf) - 1));
+  EXPECT_EQ(-1, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(-100, file.Seek(-100, SEEK_SET));
+  file.Close();
+
+  /* /testsymlink -> testdir/reffile.txt */
+  strpathinrar = itemlist[2]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlink", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK);
+
+  /*
+   * FIXME: Reading symlinks in RARs is currently broken. It takes a long time
+   * to read them and they produce erroneous results. The expected result is
+   * the target paths of the symlinks.
+   */
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(19, file.GetLength());
+  file.Close();
+
+  /* /testsymlinksubdir -> testdir/testsubdir/reffile.txt */
+  strpathinrar = itemlist[3]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlinksubdir", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(30, file.GetLength());
+  file.Close();
+
+  /* /testdir/ */
+  strpathinrar = itemlist[0]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFDIR);
+
+  itemlist.Clear();
+  ASSERT_TRUE(XFILE::CDirectory::GetDirectory(strpathinrar, itemlist));
+  itemlist.Sort(SORT_METHOD_FULLPATH, SortOrderAscending);
+
+  /* /testdir/reffile.txt */
+  strpathinrar = itemlist[1]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/reffile.txt",
+                                    true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFREG);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(0, file.GetPosition());
+  EXPECT_EQ(1616, file.GetLength());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_TRUE(file.ReadString(buf, sizeof(buf)));
+  EXPECT_EQ(39, file.GetPosition());
+  EXPECT_STREQ("an award-winning fr", buf);
+  EXPECT_EQ(100, file.Seek(100));
+  EXPECT_EQ(100, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(120, file.GetPosition());
+  EXPECT_TRUE(!memcmp("ent hub for digital ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(220, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(220, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(240, file.GetPosition());
+  EXPECT_TRUE(!memcmp("rs, XBMC is a non-pr", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1596, file.Seek(-(int64_t)sizeof(buf), SEEK_END));
+  EXPECT_EQ(1596, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_TRUE(!memcmp("multimedia jukebox.\n", buf, sizeof(buf) - 1));
+  EXPECT_EQ(-1, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(-100, file.Seek(-100, SEEK_SET));
+  file.Close();
+
+  /* /testdir/testemptysubdir */
+  strpathinrar = itemlist[2]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testemptysubdir",
+                                    true));
+  /* TODO: Should this set the itemlist to an empty list instead? */
+  EXPECT_FALSE(XFILE::CDirectory::GetDirectory(strpathinrar, itemlistemptydir));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFDIR);
+
+  /* FIXME: This directory appears a second time as a file */
+  strpathinrar = itemlist[3]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsubdir", true));
+
+  /* /testdir/testsymlink -> testsubdir/reffile.txt */
+  strpathinrar = itemlist[4]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsymlink",
+                                    true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(22, file.GetLength());
+  file.Close();
+
+  /* /testdir/testsubdir/ */
+  strpathinrar = itemlist[0]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsubdir/",
+                                    true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFDIR);
+
+  itemlist.Clear();
+  ASSERT_TRUE(XFILE::CDirectory::GetDirectory(strpathinrar, itemlist));
+  itemlist.Sort(SORT_METHOD_FULLPATH, SortOrderAscending);
+
+  /* /testdir/testsubdir/reffile.txt */
+  strpathinrar = itemlist[0]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar,
+                                    "/testdir/testsubdir/reffile.txt", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFREG);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(0, file.GetPosition());
+  EXPECT_EQ(1616, file.GetLength());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_TRUE(file.ReadString(buf, sizeof(buf)));
+  EXPECT_EQ(39, file.GetPosition());
+  EXPECT_STREQ("an award-winning fr", buf);
+  EXPECT_EQ(100, file.Seek(100));
+  EXPECT_EQ(100, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(120, file.GetPosition());
+  EXPECT_TRUE(!memcmp("ent hub for digital ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(220, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(220, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(240, file.GetPosition());
+  EXPECT_TRUE(!memcmp("rs, XBMC is a non-pr", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1596, file.Seek(-(int64_t)sizeof(buf), SEEK_END));
+  EXPECT_EQ(1596, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_TRUE(!memcmp("multimedia jukebox.\n", buf, sizeof(buf) - 1));
+  EXPECT_EQ(-1, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(-100, file.Seek(-100, SEEK_SET));
+  file.Close();
+}
+
+TEST(TestRarFile, NormalRAR)
+{
+  XFILE::CFile file;
+  char buf[20];
+  memset(&buf, 0, sizeof(buf));
+  CStdString reffile, strrarpath, strpathinrar;
+  CFileItemList itemlist, itemlistemptydir;
+  struct __stat64 stat_buffer;
+
+  reffile = XBMC_REF_FILE_PATH("xbmc/filesystem/test/refRARnormal.rar");
+  URIUtils::CreateArchivePath(strrarpath, "rar", reffile, "");
+  ASSERT_TRUE(XFILE::CDirectory::GetDirectory(strrarpath, itemlist));
+  itemlist.Sort(SORT_METHOD_FULLPATH, SortOrderAscending);
+
+  /* /reffile.txt */
+  strpathinrar = itemlist[1]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/reffile.txt", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFREG);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(0, file.GetPosition());
+  EXPECT_EQ(1616, file.GetLength());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_TRUE(file.ReadString(buf, sizeof(buf)));
+  EXPECT_EQ(39, file.GetPosition());
+  EXPECT_STREQ("an award-winning fr", buf);
+  EXPECT_EQ(100, file.Seek(100));
+  EXPECT_EQ(100, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(120, file.GetPosition());
+  EXPECT_TRUE(!memcmp("ent hub for digital ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(220, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(220, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(240, file.GetPosition());
+  EXPECT_TRUE(!memcmp("rs, XBMC is a non-pr", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1596, file.Seek(-(int64_t)sizeof(buf), SEEK_END));
+  EXPECT_EQ(1596, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_TRUE(!memcmp("multimedia jukebox.\n", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1716, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(1716, file.GetPosition());
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(-1, file.Seek(-100, SEEK_SET));
+  file.Close();
+
+  /* /testsymlink -> testdir/reffile.txt */
+  strpathinrar = itemlist[2]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlink", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK);
+
+  /*
+   * FIXME: Reading symlinks in RARs is currently broken. It takes a long time
+   * to read them and they produce erroneous results. The expected result is
+   * the target paths of the symlinks.
+   */
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(19, file.GetLength());
+  file.Close();
+
+  /* /testsymlinksubdir -> testdir/testsubdir/reffile.txt */
+  strpathinrar = itemlist[3]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlinksubdir", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(30, file.GetLength());
+  file.Close();
+
+  /* /testdir/ */
+  strpathinrar = itemlist[0]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFDIR);
+
+  itemlist.Clear();
+  ASSERT_TRUE(XFILE::CDirectory::GetDirectory(strpathinrar, itemlist));
+  itemlist.Sort(SORT_METHOD_FULLPATH, SortOrderAscending);
+
+  /* /testdir/reffile.txt */
+  strpathinrar = itemlist[1]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/reffile.txt",
+                                    true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFREG);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(0, file.GetPosition());
+  EXPECT_EQ(1616, file.GetLength());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_TRUE(file.ReadString(buf, sizeof(buf)));
+  EXPECT_EQ(39, file.GetPosition());
+  EXPECT_STREQ("an award-winning fr", buf);
+  EXPECT_EQ(100, file.Seek(100));
+  EXPECT_EQ(100, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(120, file.GetPosition());
+  EXPECT_TRUE(!memcmp("ent hub for digital ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(220, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(220, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(240, file.GetPosition());
+  EXPECT_TRUE(!memcmp("rs, XBMC is a non-pr", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1596, file.Seek(-(int64_t)sizeof(buf), SEEK_END));
+  EXPECT_EQ(1596, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_TRUE(!memcmp("multimedia jukebox.\n", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1716, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(1716, file.GetPosition());
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(-1, file.Seek(-100, SEEK_SET));
+  file.Close();
+
+  /* /testdir/testemptysubdir */
+  strpathinrar = itemlist[2]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testemptysubdir",
+                                    true));
+  /* TODO: Should this set the itemlist to an empty list instead? */
+  EXPECT_FALSE(XFILE::CDirectory::GetDirectory(strpathinrar, itemlistemptydir));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFDIR);
+
+  /* FIXME: This directory appears a second time as a file */
+  strpathinrar = itemlist[3]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsubdir", true));
+
+  /* /testdir/testsymlink -> testsubdir/reffile.txt */
+  strpathinrar = itemlist[4]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsymlink",
+                                    true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(22, file.GetLength());
+  file.Close();
+
+  /* /testdir/testsubdir/ */
+  strpathinrar = itemlist[0]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsubdir/",
+                                    true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFDIR);
+
+  itemlist.Clear();
+  ASSERT_TRUE(XFILE::CDirectory::GetDirectory(strpathinrar, itemlist));
+  itemlist.Sort(SORT_METHOD_FULLPATH, SortOrderAscending);
+
+  /* /testdir/testsubdir/reffile.txt */
+  strpathinrar = itemlist[0]->GetPath();
+  ASSERT_TRUE(StringUtils::EndsWith(strpathinrar,
+                                    "/testdir/testsubdir/reffile.txt", true));
+  EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer));
+  EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFREG);
+
+  ASSERT_TRUE(file.Open(strpathinrar));
+  EXPECT_EQ(0, file.GetPosition());
+  EXPECT_EQ(1616, file.GetLength());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_TRUE(file.ReadString(buf, sizeof(buf)));
+  EXPECT_EQ(39, file.GetPosition());
+  EXPECT_STREQ("an award-winning fr", buf);
+  EXPECT_EQ(100, file.Seek(100));
+  EXPECT_EQ(100, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(120, file.GetPosition());
+  EXPECT_TRUE(!memcmp("ent hub for digital ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(220, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(220, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(240, file.GetPosition());
+  EXPECT_TRUE(!memcmp("rs, XBMC is a non-pr", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1596, file.Seek(-(int64_t)sizeof(buf), SEEK_END));
+  EXPECT_EQ(1596, file.GetPosition());
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(1616, file.GetPosition());
+  EXPECT_TRUE(!memcmp("multimedia jukebox.\n", buf, sizeof(buf) - 1));
+  EXPECT_EQ(1716, file.Seek(100, SEEK_CUR));
+  EXPECT_EQ(1716, file.GetPosition());
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(sizeof(buf), file.Read(buf, sizeof(buf)));
+  file.Flush();
+  EXPECT_EQ(20, file.GetPosition());
+  EXPECT_TRUE(!memcmp("About\n-----\nXBMC is ", buf, sizeof(buf) - 1));
+  EXPECT_EQ(0, file.Seek(0, SEEK_SET));
+  EXPECT_EQ(-1, file.Seek(-100, SEEK_SET));
+  file.Close();
+}
 #endif /*HAS_FILESYSTEM_RAR*/
diff --git a/xbmc/filesystem/test/refRARnormal.rar b/xbmc/filesystem/test/refRARnormal.rar
new file mode 100644 (file)
index 0000000..58fe71d
Binary files /dev/null and b/xbmc/filesystem/test/refRARnormal.rar differ
diff --git a/xbmc/filesystem/test/refRARstored.rar b/xbmc/filesystem/test/refRARstored.rar
new file mode 100644 (file)
index 0000000..1500027
Binary files /dev/null and b/xbmc/filesystem/test/refRARstored.rar differ
index 05ac821..f6f2c44 100644 (file)
@@ -259,17 +259,30 @@ bool CJpegIO::Open(const CStdString &texturePath, unsigned int minx, unsigned in
   XFILE::CFile file;
   if (file.Open(m_texturePath.c_str(), READ_TRUNCATED))
   {
+    /*
+     GetLength() will typically return values that fall into three cases:
+       1. The real filesize. This is the typical case.
+       2. Zero. This is the case for some http:// streams for example.
+       3. Some value smaller than the real filesize. This is the case for an expanding file.
+
+     In order to handle all three cases, we read the file in chunks, relying on Read()
+     returning 0 at EOF.  To minimize (re)allocation of the buffer, the chunksize in
+     cases 1 and 3 is set to one byte larger** than the value returned by GetLength().
+     The chunksize in case 2 is set to the larger of 64k and GetChunkSize().
+
+     We fill the buffer entirely before reallocation.  Thus, reallocation never occurs in case 1
+     as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
+
+     To minimize reallocation, we double the chunksize each time up to a maxchunksize of 2MB.
+     */
     unsigned int filesize = (unsigned int)file.GetLength();
-    unsigned int chunksize = filesize, maxchunksize = filesize;
-    if (!chunksize)
-    { // no size information, so try reading chunked
-      chunksize = std::max(65536U, (unsigned int)file.GetChunkSize());
-      maxchunksize = std::max(chunksize, 2048*1024U); // max 2MB
-    }
-    unsigned int total = 0, amount = 0;
+    unsigned int chunksize = filesize ? (filesize + 1) : std::max(65536U, (unsigned int)file.GetChunkSize());
+    unsigned int maxchunksize = 2048*1024U; /* max 2MB chunksize */
+
+    unsigned int total_read = 0, free_space = 0;
     while (true)
     {
-      if (!amount)
+      if (!free_space)
       { // (re)alloc
         m_inputBuffSize += chunksize;
         m_inputBuff = (unsigned char *)realloc(m_inputBuff, m_inputBuffSize);
@@ -278,16 +291,16 @@ bool CJpegIO::Open(const CStdString &texturePath, unsigned int minx, unsigned in
           CLog::Log(LOGERROR, "%s unable to allocate buffer of size %u", __FUNCTION__, m_inputBuffSize);
           return false;
         }
-        amount = chunksize;
+        free_space = chunksize;
         chunksize = std::min(chunksize*2, maxchunksize);
       }
-      unsigned int read = file.Read(m_inputBuff + total, amount);
-      amount -= read;
-      total  += read;
-      if (!read || total == filesize)
+      unsigned int read = file.Read(m_inputBuff + total_read, free_space);
+      free_space -= read;
+      total_read += read;
+      if (!read)
         break;
     }
-    m_inputBuffSize = total;
+    m_inputBuffSize = total_read;
     file.Close();
 
     if (m_inputBuffSize == 0)
index 0d8ba07..20d12b3 100644 (file)
@@ -214,6 +214,7 @@ typedef enum {
   XBMCK_POWER       = 0x140,    // Power Macintosh power key
   XBMCK_EURO        = 0x141,    // Some european keyboards
   XBMCK_UNDO        = 0x142,    // Atari keyboard has Undo
+  XBMCK_SLEEP       = 0x143,    // Sleep button on Nyxboard remote (and others?)
 
   // Add any other keys here
 
index dbc28a7..aaf65ba 100644 (file)
@@ -222,6 +222,7 @@ static const XBMCKEYTABLE XBMCKeyTable[] =
 , { XBMCK_SCROLLOCK,              0,    0, XBMCVK_SCROLLLOCK,    "scrolllock" }
 , { XBMCK_PRINT,                  0,    0, XBMCVK_PRINTSCREEN,   "printscreen" }
 , { XBMCK_POWER,                  0,    0, XBMCVK_POWER,         "power" }
+, { XBMCK_SLEEP,                  0,    0, XBMCVK_SLEEP,         "sleep" }
 };
 
 static int XBMCKeyTableSize = sizeof(XBMCKeyTable)/sizeof(XBMCKEYTABLE);
index 38f675d..70b27e3 100644 (file)
@@ -205,6 +205,7 @@ typedef enum {
   XBMCVK_SCROLLLOCK     = 0xDC,
   XBMCVK_PAUSE          = 0XDD,
   XBMCVK_POWER          = 0XDE,
+  XBMCVK_SLEEP          = 0XDF,
 
   XBMCVK_LAST           = 0xFF
 } XBMCVKey;
index fb2b5e4..89f7b87 100644 (file)
@@ -262,7 +262,7 @@ JSONRPC_STATUS CPlayerOperations::Stop(const CStdString &method, ITransportLayer
   {
     case Video:
     case Audio:
-      CApplicationMessenger::Get().SendAction(CAction(ACTION_STOP));
+      CApplicationMessenger::Get().MediaStop(true, parameterObject["playerid"].asInteger());
       return ACK;
 
     case Picture:
index ecc4dcc..2ea17bc 100644 (file)
@@ -37,7 +37,7 @@ namespace XBMCAddon
     bool File::write(const char* pBuffer)
     {
       DelayedCallGuard dg(languageHook);
-      return file->Write( (void*) pBuffer, strlen( pBuffer ) + 1 ) > 0;
+      return file->Write( (void*) pBuffer, strlen( pBuffer ) ) > 0;
     }
 
   }
index 792bdad..ab58cf2 100644 (file)
@@ -53,11 +53,12 @@ public:
   virtual int vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate,
                                        HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags) = 0;
 
-  virtual int vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group, TV_SUPPORTED_MODE_T *supported_modes,
+  virtual int vc_tv_hdmi_get_supported_modes_new(HDMI_RES_GROUP_T group, TV_SUPPORTED_MODE_NEW_T *supported_modes,
                                              uint32_t max_supported_modes, HDMI_RES_GROUP_T *preferred_group,
                                              uint32_t *preferred_mode) = 0;
-  virtual int vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) = 0;
-  virtual int vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate) = 0;
+  virtual int vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code) = 0;
+  virtual int vc_tv_hdmi_set_property(const HDMI_PROPERTY_PARAM_T *property) = 0;
+  virtual int vc_tv_get_display_state(TV_DISPLAY_STATE_T *tvstate) = 0;
   virtual int vc_tv_show_info(uint32_t show) = 0;
   virtual int vc_gencmd(char *response, int maxlen, const char *string) = 0;
   virtual void vc_tv_register_callback(TVSERVICE_CALLBACK_T callback, void *callback_data) = 0;
@@ -97,14 +98,16 @@ public:
   virtual int vc_tv_hdmi_power_on_best_3d(uint32_t width, uint32_t height, uint32_t frame_rate,
                                        HDMI_INTERLACED_T scan_mode, EDID_MODE_MATCH_FLAG_T match_flags)
     { return ::vc_tv_hdmi_power_on_best_3d(width, height, frame_rate, scan_mode, match_flags); };
-  virtual int vc_tv_hdmi_get_supported_modes(HDMI_RES_GROUP_T group, TV_SUPPORTED_MODE_T *supported_modes,
+  virtual int vc_tv_hdmi_get_supported_modes_new(HDMI_RES_GROUP_T group, TV_SUPPORTED_MODE_NEW_T *supported_modes,
                                              uint32_t max_supported_modes, HDMI_RES_GROUP_T *preferred_group,
                                              uint32_t *preferred_mode)
-    { return ::vc_tv_hdmi_get_supported_modes(group, supported_modes, max_supported_modes, preferred_group, preferred_mode); };
-  virtual int vc_tv_hdmi_power_on_explicit(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code)
-    { return ::vc_tv_hdmi_power_on_explicit(mode, group, code); };
-  virtual int vc_tv_get_state(TV_GET_STATE_RESP_T *tvstate)
-    { return ::vc_tv_get_state(tvstate); };
+    { return ::vc_tv_hdmi_get_supported_modes_new(group, supported_modes, max_supported_modes, preferred_group, preferred_mode); };
+  virtual int vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_T mode, HDMI_RES_GROUP_T group, uint32_t code)
+    { return ::vc_tv_hdmi_power_on_explicit_new(mode, group, code); };
+  virtual int vc_tv_hdmi_set_property(const HDMI_PROPERTY_PARAM_T *property)
+    { return ::vc_tv_hdmi_set_property(property); };
+  virtual int vc_tv_get_display_state(TV_DISPLAY_STATE_T *tvstate)
+    { return ::vc_tv_get_display_state(tvstate); };
   virtual int vc_tv_show_info(uint32_t show)
     { return ::vc_tv_show_info(show); };
   virtual int vc_gencmd(char *response, int maxlen, const char *string)
@@ -162,10 +165,11 @@ class DllBcmHost : public DllDynamic, DllBcmHostInterface
                                                      HDMI_INTERLACED_T p4, EDID_MODE_MATCH_FLAG_T p5))
   DEFINE_METHOD5(int,     vc_tv_hdmi_power_on_best_3d, (uint32_t p1, uint32_t p2, uint32_t p3,
                                                      HDMI_INTERLACED_T p4, EDID_MODE_MATCH_FLAG_T p5))
-  DEFINE_METHOD5(int,     vc_tv_hdmi_get_supported_modes, (HDMI_RES_GROUP_T p1, TV_SUPPORTED_MODE_T *p2,
+  DEFINE_METHOD5(int,     vc_tv_hdmi_get_supported_modes_new, (HDMI_RES_GROUP_T p1, TV_SUPPORTED_MODE_NEW_T *p2,
                                                            uint32_t p3, HDMI_RES_GROUP_T *p4, uint32_t *p5))
-  DEFINE_METHOD3(int,     vc_tv_hdmi_power_on_explicit, (HDMI_MODE_T p1, HDMI_RES_GROUP_T p2, uint32_t p3))
-  DEFINE_METHOD1(int,     vc_tv_get_state, (TV_GET_STATE_RESP_T *p1))
+  DEFINE_METHOD3(int,     vc_tv_hdmi_power_on_explicit_new, (HDMI_MODE_T p1, HDMI_RES_GROUP_T p2, uint32_t p3))
+  DEFINE_METHOD1(int,     vc_tv_hdmi_set_property, (const HDMI_PROPERTY_PARAM_T *property))
+  DEFINE_METHOD1(int,     vc_tv_get_display_state, (TV_DISPLAY_STATE_T *p1))
   DEFINE_METHOD1(int,    vc_tv_show_info, (uint32_t p1))
   DEFINE_METHOD3(int,    vc_gencmd, (char *p1, int p2, const char *p3))
 
@@ -195,9 +199,10 @@ class DllBcmHost : public DllDynamic, DllBcmHostInterface
     RESOLVE_METHOD(graphics_get_display_size)
     RESOLVE_METHOD(vc_tv_hdmi_power_on_best)
     RESOLVE_METHOD(vc_tv_hdmi_power_on_best_3d)
-    RESOLVE_METHOD(vc_tv_hdmi_get_supported_modes)
-    RESOLVE_METHOD(vc_tv_hdmi_power_on_explicit)
-    RESOLVE_METHOD(vc_tv_get_state)
+    RESOLVE_METHOD(vc_tv_hdmi_get_supported_modes_new)
+    RESOLVE_METHOD(vc_tv_hdmi_power_on_explicit_new)
+    RESOLVE_METHOD(vc_tv_hdmi_set_property)
+    RESOLVE_METHOD(vc_tv_get_display_state)
     RESOLVE_METHOD(vc_tv_show_info)
     RESOLVE_METHOD(vc_gencmd)
     RESOLVE_METHOD(vc_tv_register_callback)
index 89f81b1..b53e7de 100644 (file)
@@ -417,7 +417,7 @@ static void setupWindowMenu(void)
   [pool release];
 }
 
-#define VK_SLEEP            0x5F
+#define VK_SLEEP            0x143
 #define VK_VOLUME_MUTE      0xAD
 #define VK_VOLUME_DOWN      0xAE
 #define VK_VOLUME_UP        0xAF
@@ -428,7 +428,7 @@ static void setupWindowMenu(void)
 #define VK_REWIND           0x9D
 #define VK_FAST_FWD         0x9E
 
-- (void)MediaKeyPower
+- (void)powerKeyNotification
 {
   SDL_Event event;
   memset(&event, 0, sizeof(event));
index 0ed1242..cc8405d 100644 (file)
@@ -199,6 +199,8 @@ bool CTagLoaderTagLib::Load(const string& strFileName, CMusicInfoTag& tag, Embed
     id3v2 = ttaFile->ID3v2Tag(false);
   else if (wvFile)
     ape = wvFile->APETag(false);
+  else if (mpcFile)
+    ape = mpcFile->APETag(false);
   else    // This is a catch all to get generic information for other files types (s3m, xm, it, mod, etc)
     generic = file->tag();
 
@@ -456,11 +458,11 @@ bool CTagLoaderTagLib::ParseAPETag(APE::Tag *ape, EmbeddedArt *art, CMusicInfoTa
   for (APE::ItemListMap::ConstIterator it = itemListMap.begin(); it != itemListMap.end(); ++it)
   {
     if (it->first == "ARTIST")                         SetArtist(tag, StringListToVectorString(it->second.toStringList()));
-    else if (it->first == "ALBUM ARTIST")              SetAlbumArtist(tag, StringListToVectorString(it->second.toStringList()));
+    else if (it->first == "ALBUM ARTIST" || it->first == "ALBUMARTIST") SetAlbumArtist(tag, StringListToVectorString(it->second.toStringList()));
     else if (it->first == "ALBUM")                     tag.SetAlbum(it->second.toString().to8Bit(true));
     else if (it->first == "TITLE")                     tag.SetTitle(it->second.toString().to8Bit(true));
     else if (it->first == "TRACKNUMBER" || it->first == "TRACK") tag.SetTrackNumber(it->second.toString().toInt());
-    else if (it->first == "DISCNUMBER")                tag.SetPartOfSet(it->second.toString().toInt());
+    else if (it->first == "DISCNUMBER" || it->first == "DISC") tag.SetPartOfSet(it->second.toString().toInt());
     else if (it->first == "YEAR")                      tag.SetYear(it->second.toString().toInt());
     else if (it->first == "GENRE")                     SetGenre(tag, StringListToVectorString(it->second.toStringList()));
     else if (it->first == "COMMENT")                   tag.SetComment(it->second.toString().to8Bit(true));
index 4ac6e4b..3b80475 100644 (file)
@@ -38,6 +38,9 @@ CDNSNameCache::~CDNSNameCache(void)
 
 bool CDNSNameCache::Lookup(const CStdString& strHostName, CStdString& strIpAddress)
 {
+  if (strHostName.empty() && strIpAddress.empty())
+    return false;
+
   // first see if this is already an ip address
   unsigned long address = inet_addr(strHostName.c_str());
   strIpAddress.Empty();
index 7b56c20..73b9389 100644 (file)
 #include "guilib/GUIMessage.h"
 #include "threads/SingleLock.h"
 #include "utils/log.h"
+#include "osx/DarwinUtils.h"
 
 #include <arpa/inet.h>
 #include <netinet/in.h>
 
 namespace
 {
-  CStdString CFStringToCStdString(const CFStringRef cfstr)
-  {
-    //first try the short path
-    const char *p_tmp = CFStringGetCStringPtr(cfstr, kCFStringEncodingUTF8);
-    if (p_tmp)
-      return CStdString(p_tmp);
-    
-    // i'm not sure if CFStringGetMaximumSizeForEncoding
-    // includes space for the termination character or not?
-    // so i add 1 here to make sure..
-    CFIndex buf_len = 1 + CFStringGetMaximumSizeForEncoding(
-      CFStringGetLength(cfstr), kCFStringEncodingUTF8);
-
-    char *buffer = new char[buf_len];
-    CFStringGetCString(cfstr, buffer, buf_len, kCFStringEncodingUTF8);
-    CStdString myString(buffer);
-    delete[] buffer;
-
-    return myString;
-  }
-  
   //helper for getting a the txt-records list
   //returns true on success, false if nothing found or error
   CZeroconfBrowser::ZeroconfService::tTxtRecordMap GetTxtRecords(CFNetServiceRef serviceRef)  
@@ -78,12 +58,16 @@ namespace
 
           for(idx = 0; idx < numValues; idx++)
           {
-            recordMap.insert(
-              std::make_pair(
-                CFStringToCStdString(keys[idx]),
-                CStdString((const char *)CFDataGetBytePtr(values[idx]))
-              )
-            );
+            std::string key;
+            if (DarwinCFStringRefToString(keys[idx], key))
+            {
+              recordMap.insert(
+                std::make_pair(
+                  key,
+                  CStdString((const char *)CFDataGetBytePtr(values[idx]))
+                )
+              );
+            }
           }
         }
         CFRelease(dict);
@@ -161,11 +145,19 @@ void CZeroconfBrowserOSX::BrowserCallback(CFNetServiceBrowserRef browser, CFOpti
     assert(service);
     //get our instance
     CZeroconfBrowserOSX* p_this = reinterpret_cast<CZeroconfBrowserOSX*>(info);
+
     //store the service
-    ZeroconfService s(
-      CFStringToCStdString(CFNetServiceGetName(service)),
-      CFStringToCStdString(CFNetServiceGetType(service)),
-      CFStringToCStdString(CFNetServiceGetDomain(service)));
+    std::string name, type, domain;
+    if (!DarwinCFStringRefToString(CFNetServiceGetName(service), name) ||
+        !DarwinCFStringRefToString(CFNetServiceGetType(service), type) ||
+        !DarwinCFStringRefToString(CFNetServiceGetDomain(service), domain))
+    {
+      CLog::Log(LOGWARNING, "CZeroconfBrowserOSX::BrowserCallback failed to convert service strings.");
+      return;
+    }
+
+    ZeroconfService s(name, type, domain);
+
     if (flags & kCFNetServiceFlagRemove)
     {
       CLog::Log(LOGDEBUG, "CZeroconfBrowserOSX::BrowserCallback service named: %s, type: %s, domain: %s disappeared", 
index 4bf1fd3..b8bf9ac 100644 (file)
@@ -530,7 +530,7 @@ BuildObject(CFileItem&                    item,
 
     // determine the correct artwork for this item
     if (!thumb_loader.IsNull())
-        thumb_loader->FillLibraryArt(item);
+        thumb_loader->LoadItem(&item);
 
     // finally apply the found artwork
     thumb = item.GetArt("thumb");
index 21fde4d..c47c8eb 100644 (file)
@@ -201,6 +201,7 @@ NPT_String CUPnPServer::BuildSafeResourceUri(const NPT_HttpUrl &rooturi,
     else
       filename = URIUtils::GetFileName(file_path);
 
+    CURL::Encode(filename);
     md5state.append(file_path);
     md5state.getDigest(md5);
     md5 += "/" + filename;
index 0b1b22e..8704c15 100644 (file)
@@ -57,7 +57,6 @@ extern "C"
 
   // Version.
   //
-  const char* Cocoa_GetAppVersion();
   bool Cocoa_HasVDADecoder();
   bool Cocoa_GPUForDisplayIsNvidiaPureVideo3();
   int Cocoa_GetOSVersion();
index 75b7669..cdc3873 100644 (file)
@@ -373,31 +373,6 @@ void Cocoa_ShowMouse()
 }
 
 //---------------------------------------------------------------------------------
-static char strVersion[32];
-
-const char* Cocoa_GetAppVersion()
-{
-  // Get the main bundle for the app and return the version.
-  CFBundleRef mainBundle = CFBundleGetMainBundle();
-  CFStringRef versStr = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(mainBundle, kCFBundleVersionKey);
-  
-  memset(strVersion,0,32);
-  
-  if (versStr != NULL && CFGetTypeID(versStr) == CFStringGetTypeID())
-  {
-    bool res = CFStringGetCString(versStr, strVersion, 32,kCFStringEncodingUTF8);
-    if (!res)
-    {
-      printf("Error converting version string\n");      
-      strcpy(strVersion, "SVN");
-    }
-  }
-  else
-    strcpy(strVersion, "SVN");
-  
-  return strVersion;
-}
-
 bool Cocoa_HasVDADecoder()
 {
   static int result = -1;
index 4849edc..bfd5269 100644 (file)
 
 #include <string>
 
+// We forward declare CFStringRef in order to avoid
+// pulling in tons of Objective-C headers.
+struct __CFString;
+typedef const struct __CFString * CFStringRef;
+
 #ifdef __cplusplus
 extern "C"
 {
@@ -35,6 +40,7 @@ extern "C"
   bool        DarwinHasVideoToolboxDecoder(void);
   int         DarwinBatteryLevel(void);
   void        DarwinSetScheduling(int message);
+  bool        DarwinCFStringRefToString(CFStringRef source, std::string& destination);
 #ifdef __cplusplus
 }
 #endif
index bcfb9cb..f5c30dd 100644 (file)
@@ -35,6 +35,7 @@
   #import <sys/sysctl.h>
 #else
   #import <Cocoa/Cocoa.h>
+  #import <CoreFoundation/CoreFoundation.h>
   #import <IOKit/ps/IOPowerSources.h>
   #import <IOKit/ps/IOPSKeys.h>
 #endif
@@ -316,4 +317,32 @@ void DarwinSetScheduling(int message)
   result = pthread_setschedparam(this_pthread_self, policy, &param );
 }
 
+bool DarwinCFStringRefToString(CFStringRef source, std::string &destination)
+{
+  const char *cstr = CFStringGetCStringPtr(source, CFStringGetSystemEncoding());
+  if (!cstr)
+  {
+    CFIndex strLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(source) + 1,
+                                                       CFStringGetSystemEncoding());
+    char *allocStr = (char*)malloc(strLen);
+
+    if(!allocStr)
+      return false;
+
+    if(!CFStringGetCString(source, allocStr, strLen, CFStringGetSystemEncoding()))
+    {
+      free((void*)allocStr);
+      return false;
+    }
+
+    destination = allocStr;
+    free((void*)allocStr);
+
+    return true;
+  }
+
+  destination = cstr;
+  return true;
+}
+
 #endif
index d765e59..b92c4ab 100644 (file)
@@ -43,8 +43,8 @@ NSString* const MediaKeyPreviousNotification  = @"MediaKeyPreviousNotification";
 #define kCGEventTapOptionDefault 0
 #endif
 
-#define NX_KEYSTATE_UP      0x0A
-#define NX_KEYSTATE_DOWN    0x0B
+#define NX_KEYSTATE_DOWN    0x0A
+#define NX_KEYSTATE_UP      0x0B
 
 @implementation HotKeyController
 
@@ -178,7 +178,9 @@ static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGE
   int keyState = (keyFlags & 0xFF00) >> 8;
   BOOL keyIsRepeat = (keyFlags & 0x1) > 0;
   
-  if (keyIsRepeat) 
+  // allow repeated keypresses for volume buttons
+  // all other repeated keypresses are handled by the os (is this really good?)
+  if (keyIsRepeat && keyCode != NX_KEYTYPE_SOUND_UP && keyCode != NX_KEYTYPE_SOUND_DOWN) 
     return event;
   
   NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
@@ -292,8 +294,9 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
     m_active = NO;
     m_eventPort = NULL;
     m_runLoopSource = NULL;
-    m_controlSysPower = NO;
-    m_controlSysVolume = NO;
+    // power button controls xbmc sleep button (this will also trigger the osx shutdown menu - we can't prevent this as it seems)
+    m_controlSysPower = YES;
+    m_controlSysVolume = YES; // volume keys control xbmc volume
   }
   return self;
 }
index b0e3617..2b22045 100644 (file)
@@ -21,6 +21,7 @@
 #include "PeripheralBusUSB.h"
 #include "peripherals/Peripherals.h"
 #include "utils/log.h"
+#include "osx/DarwinUtils.h"
 
 #include <sys/param.h>
 
@@ -224,7 +225,7 @@ void CPeripheralBusUSB::DeviceAttachCallback(CPeripheralBusUSB* refCon, io_itera
       result = (*interfaceInterface)->GetInterfaceClass(interfaceInterface, &bInterfaceClass);
       if (bInterfaceClass == kUSBHIDInterfaceClass || bInterfaceClass == kUSBCommunicationDataInterfaceClass)
       {
-        char ttlDeviceFilePath[MAXPATHLEN] = {0};
+        std::string ttlDeviceFilePath;
         CFStringRef deviceFilePathAsCFString;
         USBDevicePrivateData *privateDataRef;
         privateDataRef = new USBDevicePrivateData;
@@ -248,16 +249,16 @@ void CPeripheralBusUSB::DeviceAttachCallback(CPeripheralBusUSB* refCon, io_itera
               kIOServicePlane, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, kIORegistryIterateRecursively);
             if (deviceFilePathAsCFString)
             {
-              // Convert the path from a CFString to a NULL-terminated C string
-              CFStringGetCString((CFStringRef)deviceFilePathAsCFString,
-                ttlDeviceFilePath, MAXPATHLEN - 1, kCFStringEncodingASCII);
+              // Convert the path from a CFString to a std::string
+              if (!DarwinCFStringRefToString(deviceFilePathAsCFString, ttlDeviceFilePath))
+                CLog::Log(LOGWARNING, "CPeripheralBusUSB::DeviceAttachCallback failed to convert CFStringRef");
               CFRelease(deviceFilePathAsCFString);
             }
             IOObjectRelease(parent);
           }
         }
-        if (strlen(ttlDeviceFilePath))
-          privateDataRef->result.m_strLocation.Format("%s", ttlDeviceFilePath);
+        if (!ttlDeviceFilePath.empty())
+          privateDataRef->result.m_strLocation.Format("%s", ttlDeviceFilePath.c_str());
         else
           privateDataRef->result.m_strLocation.Format("%d", locationId);
 
index da1b28f..e578dc3 100644 (file)
@@ -216,6 +216,7 @@ void CPeripheralCecAdapter::Announce(AnnouncementFlag flag, const char *sender,
       CSingleLock lock(m_critSection);
       bActivateSource = (m_configuration.bActivateSource &&
           !m_bOnPlayReceived &&
+          !m_cecAdapter->IsLibCECActiveSource() &&
           (!m_preventActivateSourceOnPlay.IsValid() || CDateTime::GetCurrentDateTime() - m_preventActivateSourceOnPlay > CDateTimeSpan(0, 0, 0, CEC_SUPPRESS_ACTIVATE_SOURCE_AFTER_ON_STOP)));
       m_bOnPlayReceived = true;
     }
@@ -252,7 +253,7 @@ bool CPeripheralCecAdapter::InitialiseFeature(const PeripheralFeature feature)
     {
       // display warning: libCEC could not be loaded
       CLog::Log(LOGERROR, "%s", g_localizeStrings.Get(36017).c_str());
-      CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(36000), g_localizeStrings.Get(36029));
+      CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(36000), g_localizeStrings.Get(36017));
       delete m_dll;
       m_dll = NULL;
       m_features.clear();
index dcb10b6..369b4c1 100644 (file)
@@ -242,7 +242,7 @@ bool CPicture::ScaleImage(uint8_t *in_pixels, unsigned int in_width, unsigned in
 bool CPicture::OrientateImage(uint32_t *&pixels, unsigned int &width, unsigned int &height, int orientation)
 {
   // ideas for speeding these functions up: http://cgit.freedesktop.org/pixman/tree/pixman/pixman-fast-path.c
-  uint32_t *out = NULL;
+  bool out = false;
   switch (orientation)
   {
     case 1:
@@ -270,16 +270,10 @@ bool CPicture::OrientateImage(uint32_t *&pixels, unsigned int &width, unsigned i
       CLog::Log(LOGERROR, "Unknown orientation %i", orientation);
       break;
   }
-  if (out)
-  {
-    pixels = out;
-    std::swap(width, height);
-    return true;
-  }
-  return false;
+  return out;
 }
 
-uint32_t *CPicture::FlipHorizontal(uint32_t *pixels, unsigned int width, unsigned int height)
+bool CPicture::FlipHorizontal(uint32_t *&pixels, unsigned int &width, unsigned int &height)
 {
   // this can be done in-place easily enough
   for (unsigned int y = 0; y < height; ++y)
@@ -288,10 +282,10 @@ uint32_t *CPicture::FlipHorizontal(uint32_t *pixels, unsigned int width, unsigne
     for (unsigned int x = 0; x < width / 2; ++x)
       std::swap(line[x], line[width - 1 - x]);
   }
-  return NULL;
+  return true;
 }
 
-uint32_t *CPicture::FlipVertical(uint32_t *pixels, unsigned int width, unsigned int height)
+bool CPicture::FlipVertical(uint32_t *&pixels, unsigned int &width, unsigned int &height)
 {
   // this can be done in-place easily enough
   for (unsigned int y = 0; y < height / 2; ++y)
@@ -301,10 +295,10 @@ uint32_t *CPicture::FlipVertical(uint32_t *pixels, unsigned int width, unsigned
     for (unsigned int x = 0; x < width; ++x)
       std::swap(*line1++, *line2++);
   }
-  return NULL;
+  return true;
 }
 
-uint32_t *CPicture::Rotate180CCW(uint32_t *pixels, unsigned int width, unsigned int height)
+bool CPicture::Rotate180CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height)
 {
   // this can be done in-place easily enough
   for (unsigned int y = 0; y < height / 2; ++y)
@@ -320,10 +314,10 @@ uint32_t *CPicture::Rotate180CCW(uint32_t *pixels, unsigned int width, unsigned
     for (unsigned int x = 0; x < width / 2; ++x)
       std::swap(line[x], line[width - 1 - x]);
   }
-  return NULL;
+  return true;
 }
 
-uint32_t *CPicture::Rotate90CCW(uint32_t *pixels, unsigned int width, unsigned int height)
+bool CPicture::Rotate90CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height)
 {
   uint32_t *dest = new uint32_t[width * height * 4];
   if (dest)
@@ -339,16 +333,19 @@ uint32_t *CPicture::Rotate90CCW(uint32_t *pixels, unsigned int width, unsigned i
         src += width;
       }
     }
+    delete[] pixels;
+    pixels = dest;
+    std::swap(width, height);
+    return true;
   }
-  delete[] pixels;
-  return dest;
+  return false;
 }
 
-uint32_t *CPicture::Rotate270CCW(uint32_t *pixels, unsigned int width, unsigned int height)
+bool CPicture::Rotate270CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height)
 {
   uint32_t *dest = new uint32_t[width * height * 4];
   if (!dest)
-    return NULL;
+    return false;
 
   unsigned int d_height = width, d_width = height;
   for (unsigned int y = 0; y < d_height; y++)
@@ -363,14 +360,16 @@ uint32_t *CPicture::Rotate270CCW(uint32_t *pixels, unsigned int width, unsigned
   }
 
   delete[] pixels;
-  return dest;
+  pixels = dest;
+  std::swap(width, height);
+  return true;
 }
 
-uint32_t *CPicture::Transpose(uint32_t *pixels, unsigned int width, unsigned int height)
+bool CPicture::Transpose(uint32_t *&pixels, unsigned int &width, unsigned int &height)
 {
   uint32_t *dest = new uint32_t[width * height * 4];
   if (!dest)
-    return NULL;
+    return false;
 
   unsigned int d_height = width, d_width = height;
   for (unsigned int y = 0; y < d_height; y++)
@@ -385,14 +384,16 @@ uint32_t *CPicture::Transpose(uint32_t *pixels, unsigned int width, unsigned int
   }
 
   delete[] pixels;
-  return dest;
+  pixels = dest;
+  std::swap(width, height);
+  return true;
 }
 
-uint32_t *CPicture::TransposeOffAxis(uint32_t *pixels, unsigned int width, unsigned int height)
+bool CPicture::TransposeOffAxis(uint32_t *&pixels, unsigned int &width, unsigned int &height)
 {
   uint32_t *dest = new uint32_t[width * height * 4];
   if (!dest)
-    return NULL;
+    return false;
 
   unsigned int d_height = width, d_width = height;
   for (unsigned int y = 0; y < d_height; y++)
@@ -407,5 +408,7 @@ uint32_t *CPicture::TransposeOffAxis(uint32_t *pixels, unsigned int width, unsig
   }
 
   delete[] pixels;
-  return dest;
+  pixels = dest;
+  std::swap(width, height);
+  return true;
 }
index 2e571a9..e9d34da 100644 (file)
@@ -50,13 +50,13 @@ private:
                          uint8_t *out_pixels, unsigned int out_width, unsigned int out_height, unsigned int out_pitch);
   static bool OrientateImage(uint32_t *&pixels, unsigned int &width, unsigned int &height, int orientation);
 
-  static uint32_t *FlipHorizontal(uint32_t *pixels, unsigned int width, unsigned int height);
-  static uint32_t *FlipVertical(uint32_t *pixels, unsigned int width, unsigned int height);
-  static uint32_t *Rotate90CCW(uint32_t *pixels, unsigned int width, unsigned int height);
-  static uint32_t *Rotate270CCW(uint32_t *pixels, unsigned int width, unsigned int height);
-  static uint32_t *Rotate180CCW(uint32_t *pixels, unsigned int width, unsigned int height);
-  static uint32_t *Transpose(uint32_t *pixels, unsigned int width, unsigned int height);
-  static uint32_t *TransposeOffAxis(uint32_t *pixels, unsigned int width, unsigned int height);
+  static bool FlipHorizontal(uint32_t *&pixels, unsigned int &width, unsigned int &height);
+  static bool FlipVertical(uint32_t *&pixels, unsigned int &width, unsigned int &height);
+  static bool Rotate90CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height);
+  static bool Rotate270CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height);
+  static bool Rotate180CCW(uint32_t *&pixels, unsigned int &width, unsigned int &height);
+  static bool Transpose(uint32_t *&pixels, unsigned int &width, unsigned int &height);
+  static bool TransposeOffAxis(uint32_t *&pixels, unsigned int &width, unsigned int &height);
 };
 
 //this class calls CreateThumbnailFromSurface in a CJob, so a png file can be written without halting the render thread
index 4e91c60..a5534c9 100644 (file)
@@ -21,6 +21,7 @@
 #include "system.h"
 #include "PowerManager.h"
 #include "Application.h"
+#include "cores/AudioEngine/AEFactory.h"
 #include "input/KeyboardStat.h"
 #include "settings/GUISettings.h"
 #include "windowing/WindowingFactory.h"
@@ -206,6 +207,7 @@ void CPowerManager::OnSleep()
   g_application.StopPlaying();
   g_application.StopShutdownTimer();
   g_application.StopScreenSaverTimer();
+  CAEFactory::Suspend();
 }
 
 void CPowerManager::OnWake()
@@ -218,10 +220,10 @@ void CPowerManager::OnWake()
 #if defined(HAS_SDL) || defined(TARGET_WINDOWS)
   if (g_Windowing.IsFullScreen())
   {
-#ifdef _WIN32
+#if defined(_WIN32)
     ShowWindow(g_hWnd,SW_RESTORE);
     SetForegroundWindow(g_hWnd);
-#else
+#elif !defined(TARGET_DARWIN_OSX)
     // Hack to reclaim focus, thus rehiding system mouse pointer.
     // Surely there's a better way?
     g_graphicsContext.ToggleFullScreenRoot();
@@ -245,6 +247,7 @@ void CPowerManager::OnWake()
   g_lcd->Initialize();
 #endif
 
+  CAEFactory::Resume();
   g_application.UpdateLibraries();
   g_weatherManager.Refresh();
 
index 395757c..3622985 100644 (file)
@@ -609,8 +609,19 @@ bool CPVRDatabase::RemoveStaleChannelsFromGroup(const CPVRChannelGroup &group)
   if (!group.IsInternalGroup())
   {
     /* First remove channels that don't exist in the main channels table */
-    CStdString strWhereClause = FormatSQL("idChannel IN (SELECT m.idChannel FROM map_channelgroups_channels m LEFT JOIN channels on m.idChannel = channels.idChannel WHERE channels.idChannel IS NULL)");
-    bDelete = DeleteValues("map_channelgroups_channels", strWhereClause);
+
+    // XXX work around for frodo: fix this up so it uses one query for all db types
+    // mysql doesn't support subqueries when deleting and sqlite doesn't support joins when deleting
+    if (g_advancedSettings.m_databaseTV.type.Equals("mysql"))
+    {
+      CStdString strQuery = FormatSQL("DELETE m FROM map_channelgroups_channels m LEFT JOIN channels c ON (c.idChannel = m.idChannel) WHERE c.idChannel IS NULL");
+      bDelete = ExecuteQuery(strQuery);
+    }
+    else
+    {
+      CStdString strWhereClause = FormatSQL("idChannel IN (SELECT m.idChannel FROM map_channelgroups_channels m LEFT JOIN channels on m.idChannel = channels.idChannel WHERE channels.idChannel IS NULL)");
+      bDelete = DeleteValues("map_channelgroups_channels", strWhereClause);
+    }
   }
 
   if (group.m_members.size() > 0)
index cfa00f4..730b44b 100644 (file)
@@ -181,7 +181,11 @@ CPVRChannelGroupPtr CGUIWindowPVRChannels::SelectNextGroup(void)
 {
   CPVRChannelGroupPtr currentGroup = SelectedGroup();
   CPVRChannelGroupPtr nextGroup = currentGroup->GetNextGroup();
-  while (nextGroup && *nextGroup != *currentGroup && nextGroup->Size() == 0)
+  while (nextGroup && nextGroup->Size() == 0 &&
+      // break if the group matches
+      *nextGroup != *currentGroup &&
+      // or if we hit the first group
+      !nextGroup->IsInternalGroup())
     nextGroup = nextGroup->GetNextGroup();
 
   /* always update so users can reset the list */
index 2940ceb..3b9f0cc 100644 (file)
@@ -190,8 +190,9 @@ void CEdenVideoArtUpdater::Process()
       {
         std::string cachedSeason = GetCachedSeasonThumb(j->first, item->GetVideoInfoTag()->m_strPath);
         std::string type;
-        if (CacheTexture(j->second.begin()->second, cachedSeason, "", type))
-          db.SetArtForItem(idSeason, "season", type, j->second.begin()->second);
+        std::string originalUrl = j->second.begin()->second;
+        if (CacheTexture(originalUrl, cachedSeason, "", type))
+          db.SetArtForItem(idSeason, "season", type, originalUrl);
       }
     }
 
@@ -278,24 +279,24 @@ void CEdenVideoArtUpdater::Process()
   items.Clear();
 }
 
-bool CEdenVideoArtUpdater::CacheTexture(const std::string &originalUrl, const std::string &cachedFile, const std::string &label)
+bool CEdenVideoArtUpdater::CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label)
 {
   std::string type;
   return CacheTexture(originalUrl, cachedFile, label, type);
 }
 
-bool CEdenVideoArtUpdater::CacheTexture(const std::string &originalUrl, const std::string &cachedFile, const std::string &label, std::string &type)
+bool CEdenVideoArtUpdater::CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label, std::string &type)
 {
-  if (originalUrl.empty())
-  {
-    CLog::Log(LOGERROR, "%s No original url for item %s", __FUNCTION__, label.c_str());
-    return false;
-  }
   if (!CFile::Exists(cachedFile))
   {
     CLog::Log(LOGERROR, "%s No cached art for item %s (should be %s)", __FUNCTION__, label.c_str(), cachedFile.c_str());
     return false;
   }
+  if (originalUrl.empty())
+  {
+    originalUrl = GetThumb(cachedFile, "http://unknown/video/", true);
+    CLog::Log(LOGERROR, "%s No original url for item %s, but cached art exists, using %s", __FUNCTION__, label.c_str(), originalUrl.c_str());
+  }
 
   CTextureDetails details;
   details.updateable = false;
index ce2f0de..a34b4f0 100644 (file)
@@ -37,13 +37,13 @@ public:
 
 private:
   /*! \brief Caches the texture from oldCachedFile as if it came from originalUrl into the texture cache.
-   \param originalUrl the url that we think the oldCachedFile came from.
+   \param originalUrl [in/out] the url that we think the oldCachedFile came from. May be set if it's empty and an oldCachedFile exists.
    \param oldCachedFile the old cached file
    \param label the label of the item for logging
    \param type [out] the type of art (poster/banner/thumb)
    */
-  bool CacheTexture(const std::string &originalUrl, const std::string &cachedFile, const std::string &label, std::string &type);
-  bool CacheTexture(const std::string &originalUrl, const std::string &oldCachedFile, const std::string &label);
+  bool CacheTexture(std::string &originalUrl, const std::string &cachedFile, const std::string &label, std::string &type);
+  bool CacheTexture(std::string &originalUrl, const std::string &oldCachedFile, const std::string &label);
 
   CStdString GetCachedActorThumb(const CFileItem &item);
   CStdString GetCachedSeasonThumb(int season, const CStdString &path);
index 617da1a..f166a12 100644 (file)
@@ -8280,14 +8280,23 @@ void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = f
             }
           }
         }
-
+      }
+      if (singleFiles)
+      {
         xmlDoc.Clear();
         TiXmlDeclaration decl("1.0", "UTF-8", "yes");
         xmlDoc.InsertEndChild(decl);
       }
 
-      if (singleFiles && images && !bSkip)
+      if (images && !bSkip)
       {
+        if (!singleFiles)
+        {
+          CStdString strFileName(movie.m_strTitle);
+          if (movie.m_iYear > 0)
+            strFileName.AppendFormat("_%i", movie.m_iYear);
+          item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
+        }
         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
         {
           CStdString savedThumb = item.GetLocalArt(i->first, false);
@@ -8369,13 +8378,22 @@ void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = f
             }
           }
         }
-
+      }
+      if (singleFiles)
+      {
         xmlDoc.Clear();
         TiXmlDeclaration decl("1.0", "UTF-8", "yes");
         xmlDoc.InsertEndChild(decl);
       }
-      if (singleFiles && images && !bSkip)
+      if (images && !bSkip)
       {
+        if (!singleFiles)
+        {
+          CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
+          if (movie.m_iYear > 0)
+            strFileName.AppendFormat("_%i", movie.m_iYear);
+          item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
+        }
         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
         {
           CStdString savedThumb = item.GetLocalArt(i->first, false);
@@ -8471,13 +8489,18 @@ void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = f
             }
           }
         }
-
+      }
+      if (singleFiles)
+      {
         xmlDoc.Clear();
         TiXmlDeclaration decl("1.0", "UTF-8", "yes");
         xmlDoc.InsertEndChild(decl);
       }
-      if (singleFiles && images && !bSkip)
+      if (images && !bSkip)
       {
+        if (!singleFiles)
+          item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
+
         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
         {
           CStdString savedThumb = item.GetLocalArt(i->first, true);
@@ -8569,14 +8592,22 @@ void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = f
               }
             }
           }
-
+        }
+        if (singleFiles)
+        {
           xmlDoc.Clear();
           TiXmlDeclaration decl("1.0", "UTF-8", "yes");
           xmlDoc.InsertEndChild(decl);
         }
 
-        if (singleFiles && images && !bSkip)
+        if (images && !bSkip)
         {
+          if (!singleFiles)
+          {
+            CStdString epName;
+            epName.Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
+            item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
+          }
           for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
           {
             CStdString savedThumb = item.GetLocalArt(i->first, false);
@@ -8760,28 +8791,30 @@ void CVideoDatabase::ImportFromXML(const CStdString &path)
       {
         info.Load(movie);
         CFileItem item(info);
-        map<string, string> artwork;
-        if (ImportArtFromXML(movie->FirstChild("art"), artwork))
-          item.SetArt(artwork);
         bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.GetPath()) : false;
+        CStdString filename = info.m_strTitle;
+        if (info.m_iYear > 0)
+          filename.AppendFormat("_%i", info.m_iYear);
+        CFileItem artItem(item);
+        artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
+        scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
+        item.SetArt(artItem.GetArt());
         scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
-        CStdString strFileName(info.m_strTitle);
-        if (iVersion >= 1 && info.m_iYear > 0)
-          strFileName.AppendFormat("_%i", info.m_iYear);
         current++;
       }
       else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
       {
         info.Load(movie);
         CFileItem item(info);
-        map<string, string> artwork;
-        if (ImportArtFromXML(movie->FirstChild("art"), artwork))
-          item.SetArt(artwork);
         bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.GetPath()) : false;
+        CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
+        if (info.m_iYear > 0)
+          filename.AppendFormat("_%i", info.m_iYear);
+        CFileItem artItem(item);
+        artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
+        scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
+        item.SetArt(artItem.GetArt());
         scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
-        CStdString strFileName(StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle);
-        if (iVersion >= 1 && info.m_iYear > 0)
-          strFileName.AppendFormat("_%i", info.m_iYear);
         current++;
       }
       else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
@@ -8792,30 +8825,21 @@ void CVideoDatabase::ImportFromXML(const CStdString &path)
         URIUtils::AddSlashAtEnd(info.m_strPath);
         DeleteTvShow(info.m_strPath);
         CFileItem showItem(info);
-        map<string, string> artwork;
-        if (ImportArtFromXML(movie->FirstChild("art"), artwork))
-          showItem.SetArt(artwork);
         bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(showItem.GetPath(), true) : false;
+        CFileItem artItem(showItem);
+        CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
+        artItem.SetPath(artPath);
+        scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
+        showItem.SetArt(artItem.GetArt());
         int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
         // season artwork
-        TiXmlNode *art = movie->FirstChild("art");
-        if (art)
+        map<int, map<string, string> > seasonArt;
+        artItem.GetVideoInfoTag()->m_strPath = artPath;
+        scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
+        for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
         {
-          TiXmlElement *season = art->FirstChildElement("season");
-          while (season)
-          {
-            if (season->FirstChild())
-            {
-              int seasonNum = -1;
-              season->Attribute("num", &seasonNum);
-              map<string, string> artwork;
-              
-              int seasonID = AddSeason(showID, seasonNum);
-              if (ImportArtFromXML(season, artwork) &&  seasonID > -1)
-                SetArtForItem(seasonID, "season", artwork);
-            }
-            season = season->NextSiblingElement("season");
-          }
+          int seasonID = AddSeason(showID, i->first);
+          SetArtForItem(seasonID, "season", i->second);
         }
         current++;
         // now load the episodes
@@ -8826,9 +8850,12 @@ void CVideoDatabase::ImportFromXML(const CStdString &path)
           CVideoInfoTag info;
           info.Load(episode);
           CFileItem item(info);
-          map<string, string> artwork;
-          if (ImportArtFromXML(episode->FirstChild("art"), artwork))
-            item.SetArt(artwork);
+          CStdString filename;
+          filename.Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
+          CFileItem artItem(item);
+          artItem.SetPath(GetSafeFile(artPath, filename));
+          scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
+          item.SetArt(artItem.GetArt());
           scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
           episode = episode->NextSiblingElement("episodedetails");
         }
index a9b236a..fbca835 100644 (file)
@@ -1048,7 +1048,9 @@ namespace VIDEO
     if (!m_database.Open())
       return -1;
 
-    GetArtwork(pItem, content, videoFolder, useLocal, showInfo ? showInfo->m_strPath : "");
+    if (!libraryImport)
+      GetArtwork(pItem, content, videoFolder, useLocal, showInfo ? showInfo->m_strPath : "");
+
     // ensure the art map isn't completely empty by specifying an empty thumb
     map<string, string> art = pItem->GetArt();
     if (art.empty())
@@ -1100,12 +1102,14 @@ namespace VIDEO
     {
       if (pItem->m_bIsFolder)
       {
-        // get and cache season thumbs
         map<int, map<string, string> > seasonArt;
-        GetSeasonThumbs(movieDetails, seasonArt, CVideoThumbLoader::GetArtTypes("season"), useLocal);
-        for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
-          for (map<string, string>::iterator j = i->second.begin(); j != i->second.end(); ++j)
-            CTextureCache::Get().BackgroundCacheImage(j->second);
+        if (!libraryImport)
+        { // get and cache season thumbs
+          GetSeasonThumbs(movieDetails, seasonArt, CVideoThumbLoader::GetArtTypes("season"), useLocal);
+          for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
+            for (map<string, string>::iterator j = i->second.begin(); j != i->second.end(); ++j)
+              CTextureCache::Get().BackgroundCacheImage(j->second);
+        }
         lResult = m_database.SetDetailsForTvShow(pItem->GetPath(), movieDetails, art, seasonArt);
         movieDetails.m_iDbId = lResult;
         movieDetails.m_type = "tvshow";
index 4e94d4b..cc0646f 100644 (file)
@@ -2,11 +2,10 @@ dnl Process this file with autoconf to produce a configure script.
 
 AC_INIT(configure.in)
 AM_INIT_AUTOMAKE(goom2, 0.1)
-AM_CONFIG_HEADER(config.h)
+AC_CONFIG_HEADERS(config.h)
 
 AC_ISC_POSIX
 AC_PROG_CC
-AM_PROG_CC_STDC
 AC_HEADER_STDC
 AC_C_BIGENDIAN
 
index d376640..bbf1e1a 100644 (file)
@@ -1521,7 +1521,7 @@ void CWIN32Util::CropSource(CRect& src, CRect& dst, CRect target)
 
 void CWinIdleTimer::StartZero()
 {
-  SetThreadExecutionState(ES_SYSTEM_REQUIRED);
+  SetThreadExecutionState(ES_SYSTEM_REQUIRED|ES_DISPLAY_REQUIRED);
   CStopWatch::StartZero();
 }
 
index 0acf810..c3c385f 100644 (file)
@@ -69,7 +69,7 @@ static uint16_t SymMappingsEvdev[][2] =
 , { 138, 0x69 /* 'i' */}             // Info
 , { 147, 0x6d /* 'm' */}             // Menu
 , { 148, XBMCK_LAUNCH_APP2 }         // Launch app 2
-, { 150, 0x9f }                      // Sleep
+, { 150, XBMCK_SLEEP }               // Sleep
 , { 152, XBMCK_LAUNCH_APP1 }         // Launch app 1
 , { 163, XBMCK_LAUNCH_MAIL }         // Launch Mail
 , { 164, XBMCK_BROWSER_FAVORITES }   // Browser favorites
index 4a2d358..4184665 100644 (file)
     m == 38 || m == 43 || m == 45 || m == 49 || m == 51 || \
     m == 53 || m == 55 || m == 57 || m == 59)
 
-#define MAKEFLAGS(group, mode, interlace, mode3d) \
+#define MAKEFLAGS(group, mode, interlace) \
   ( ( (mode)<<24 ) | ( (group)<<16 ) | \
    ( (interlace) != 0 ? D3DPRESENTFLAG_INTERLACED : D3DPRESENTFLAG_PROGRESSIVE) | \
-   ( ((group) == HDMI_RES_GROUP_CEA && IS_WIDESCREEN(mode) ) ? D3DPRESENTFLAG_WIDESCREEN : 0) | \
-   ( (mode3d) != 0 ? D3DPRESENTFLAG_MODE3DSBS : 0 ))
+   ( ((group) == HDMI_RES_GROUP_CEA && IS_WIDESCREEN(mode) ) ? D3DPRESENTFLAG_WIDESCREEN : 0) )
 
-#define GETFLAGS_INTERLACE(f)   ( ( (f) & D3DPRESENTFLAG_INTERLACED ) != 0)
-#define GETFLAGS_WIDESCREEN(f)  ( ( (f) & D3DPRESENTFLAG_WIDESCREEN ) != 0)
 #define GETFLAGS_GROUP(f)       ( (HDMI_RES_GROUP_T)( ((f) >> 16) & 0xff ))
 #define GETFLAGS_MODE(f)        ( ( (f) >>24 ) & 0xff )
-#define GETFLAGS_MODE3D(f)      ( ( (f) & D3DPRESENTFLAG_MODE3DSBS ) !=0 )
-
-#define TV_MAX_SUPPORTED_MODES 60
 
 //#ifndef DEBUG_PRINT
 //#define DEBUG_PRINT 1
@@ -182,62 +176,86 @@ bool CEGLNativeTypeRaspberryPI::GetNativeResolution(RESOLUTION_INFO *res) const
 #endif
 }
 
-bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
+#if defined(TARGET_RASPBERRY_PI)
+int CEGLNativeTypeRaspberryPI::FindMatchingResolution(const RESOLUTION_INFO &res, const std::vector<RESOLUTION_INFO> &resolutions)
 {
+  for (int i = 0; i < (int)resolutions.size(); i++)
+  {
+    if(resolutions[i].iScreenWidth == res.iScreenWidth && resolutions[i].iScreenHeight == res.iScreenHeight && resolutions[i].fRefreshRate == res.fRefreshRate)
+    {
+       return i;
+    }
+  }
+  return -1;
+}
+#endif
+
 #if defined(TARGET_RASPBERRY_PI)
-  bool bFound = false;
+int CEGLNativeTypeRaspberryPI::AddUniqueResolution(const RESOLUTION_INFO &res, std::vector<RESOLUTION_INFO> &resolutions)
+{
+  int i = FindMatchingResolution(res, resolutions);
+  if (i>=0)
+  {  // don't replace a progressive resolution with an interlaced one of same resolution
+     if (!(res.dwFlags & D3DPRESENTFLAG_INTERLACED))
+       resolutions[i] = res;
+  }
+  else
+  {
+     resolutions.push_back(res);
+  }
+  return i;
+}
+#endif
 
+bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
+{
+#if defined(TARGET_RASPBERRY_PI)
   if(!m_DllBcmHost || !m_nativeWindow)
     return false;
 
   DestroyDispmaxWindow();
 
-  RESOLUTION_INFO resSearch;
-
-  int best_score = 0;
-
-  for (size_t i = 0; i < m_res.size(); i++)
-  {
-    if(m_res[i].iScreenWidth == res.iScreenWidth && m_res[i].iScreenHeight == res.iScreenHeight && m_res[i].fRefreshRate == res.fRefreshRate)
-    {
-      int score = 0;
-
-      /* prefere progressive over interlaced */
-      if(!GETFLAGS_INTERLACE(m_res[i].dwFlags))
-        score = 1;
-
-      if(score >= best_score)
-      {
-        resSearch = m_res[i];
-        bFound = true;
-      }
-    }
-  }
-
-  if(bFound && !m_fixedMode)
+  if(!m_fixedMode && GETFLAGS_GROUP(res.dwFlags) && GETFLAGS_MODE(res.dwFlags))
   {
     sem_init(&m_tv_synced, 0, 0);
     m_DllBcmHost->vc_tv_register_callback(CallbackTvServiceCallback, this);
 
-    int success = m_DllBcmHost->vc_tv_hdmi_power_on_explicit(HDMI_MODE_HDMI, GETFLAGS_GROUP(resSearch.dwFlags), GETFLAGS_MODE(resSearch.dwFlags));
+    if (res.dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB))
+    {
+      /* inform TV of any 3D settings. Note this property just applies to next hdmi mode change, so no need to call for 2D modes */
+      HDMI_PROPERTY_PARAM_T property;
+      property.property = HDMI_PROPERTY_3D_STRUCTURE;
+      if (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
+        property.param1 = HDMI_3D_FORMAT_SBS_HALF;
+      else if (res.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+        property.param1 = HDMI_3D_FORMAT_TB_HALF;
+      else
+        property.param1 = HDMI_3D_FORMAT_NONE;
+      property.param2 = 0;
+      vc_tv_hdmi_set_property(&property);
+    }
+    int success = m_DllBcmHost->vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags));
 
     if (success == 0)
     {
-      CLog::Log(LOGDEBUG, "EGL set HDMI mode (%d,%d,%d)=%d\n",
-                          GETFLAGS_MODE3D(resSearch.dwFlags) ? HDMI_MODE_3D:HDMI_MODE_HDMI, GETFLAGS_GROUP(resSearch.dwFlags),
-                          GETFLAGS_MODE(resSearch.dwFlags), success);
+      CLog::Log(LOGDEBUG, "EGL set HDMI mode (%d,%d)=%d%s%s\n",
+                          GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), success,
+                          (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ? " SBS":"",
+                          (res.dwFlags & D3DPRESENTFLAG_MODE3DTB) ? " TB":"");
+
       sem_wait(&m_tv_synced);
     }
     else
     {
-      CLog::Log(LOGERROR, "EGL failed to set HDMI mode (%d,%d,%d)=%d\n",
-                          GETFLAGS_MODE3D(resSearch.dwFlags) ? HDMI_MODE_3D:HDMI_MODE_HDMI, GETFLAGS_GROUP(resSearch.dwFlags),
-                          GETFLAGS_MODE(resSearch.dwFlags), success);
+      CLog::Log(LOGERROR, "EGL failed to set HDMI mode (%d,%d)=%d%s%s\n",
+                          GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), success,
+                          (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ? " SBS":"",
+                          (res.dwFlags & D3DPRESENTFLAG_MODE3DTB) ? " TB":"");
     }
     m_DllBcmHost->vc_tv_unregister_callback(CallbackTvServiceCallback);
     sem_destroy(&m_tv_synced);
 
-    m_desktopRes = resSearch;
+    m_desktopRes = res;
   }
 
   m_dispman_display = m_DllBcmHost->vc_dispmanx_display_open(0);
@@ -268,16 +286,23 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
   DISPMANX_TRANSFORM_T transform = DISPMANX_NO_ROTATE;
   DISPMANX_UPDATE_HANDLE_T dispman_update = m_DllBcmHost->vc_dispmanx_update_start(0);
 
-  CLog::Log(LOGDEBUG, "EGL set resolution %dx%d -> %dx%d @ %.2f fps\n",
-      m_width, m_height, dst_rect.width, dst_rect.height, bFound ? resSearch.fRefreshRate : res.fRefreshRate);
+  CLog::Log(LOGDEBUG, "EGL set resolution %dx%d -> %dx%d @ %.2f fps (%d,%d) flags:%x aspect:%.2f\n",
+      m_width, m_height, dst_rect.width, dst_rect.height, res.fRefreshRate, GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), (int)res.dwFlags, res.fPixelRatio);
 
   // The trick for SBS is that we stick two dispman elements together 
-  // and the PI firmware knows that we are in SBS mode and it renders the gui in SBS
-  if(bFound && (resSearch.dwFlags & D3DPRESENTFLAG_MODE3DSBS))
+  // and the PI firmware knows that we are in SBS/TAB mode and it renders the gui in SBS/TAB
+  if(res.dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB))
   {
-    // right side
-    dst_rect.x = res.iScreenWidth;
-    dst_rect.width = res.iScreenWidth;
+    if (res.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+    {
+      // bottom half
+      dst_rect.y = res.iScreenHeight;
+    }
+    else
+    {
+      // right side
+      dst_rect.x = res.iScreenWidth;
+    }
 
     m_dispman_element2 = m_DllBcmHost->vc_dispmanx_element_add(dispman_update,
       m_dispman_display,
@@ -292,9 +317,16 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
       assert(m_dispman_element2 != DISPMANX_NO_HANDLE);
       assert(m_dispman_element2 != (unsigned)DISPMANX_INVALID);
 
-    // left side - fall through
-    dst_rect.x = 0;
-    dst_rect.width = res.iScreenWidth;
+    if (res.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+    {
+      // top half - fall through
+      dst_rect.y = 0;
+    }
+    else
+    {
+      // left side - fall through
+      dst_rect.x = 0;
+    }
   }
 
   m_dispman_element = m_DllBcmHost->vc_dispmanx_element_add(dispman_update,
@@ -330,11 +362,40 @@ bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
 #endif
 }
 
+#if defined(TARGET_RASPBERRY_PI)
+static float get_display_aspect_ratio(HDMI_ASPECT_T aspect)
+{
+  float display_aspect;
+  switch (aspect) {
+    case HDMI_ASPECT_4_3:   display_aspect = 4.0/3.0;   break;
+    case HDMI_ASPECT_14_9:  display_aspect = 14.0/9.0;  break;
+    case HDMI_ASPECT_16_9:  display_aspect = 16.0/9.0;  break;
+    case HDMI_ASPECT_5_4:   display_aspect = 5.0/4.0;   break;
+    case HDMI_ASPECT_16_10: display_aspect = 16.0/10.0; break;
+    case HDMI_ASPECT_15_9:  display_aspect = 15.0/9.0;  break;
+    case HDMI_ASPECT_64_27: display_aspect = 64.0/27.0; break;
+    default:                display_aspect = 16.0/9.0;  break;
+  }
+  return display_aspect;
+}
+
+static float get_display_aspect_ratio(SDTV_ASPECT_T aspect)
+{
+  float display_aspect;
+  switch (aspect) {
+    case SDTV_ASPECT_4_3:  display_aspect = 4.0/3.0;  break;
+    case SDTV_ASPECT_14_9: display_aspect = 14.0/9.0; break;
+    case SDTV_ASPECT_16_9: display_aspect = 16.0/9.0; break;
+    default:               display_aspect = 4.0/3.0;  break;
+  }
+  return display_aspect;
+}
+#endif
+
 bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &resolutions)
 {
 #if defined(TARGET_RASPBERRY_PI)
   resolutions.clear();
-  m_res.clear();
 
   if(!m_DllBcmHost)
     return false;
@@ -349,25 +410,56 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
 
   if(m_initDesktopRes)
   {
-    TV_GET_STATE_RESP_T tv_state;
+    TV_DISPLAY_STATE_T tv_state;
 
     // get current display settings state
-    memset(&tv_state, 0, sizeof(TV_GET_STATE_RESP_T));
-    m_DllBcmHost->vc_tv_get_state(&tv_state);
-
-    m_desktopRes.iScreen      = 0;
-    m_desktopRes.bFullScreen  = true;
-    m_desktopRes.iWidth       = tv_state.width;
-    m_desktopRes.iHeight      = tv_state.height;
-    m_desktopRes.iScreenWidth = tv_state.width;
-    m_desktopRes.iScreenHeight= tv_state.height;
-    m_desktopRes.dwFlags      = tv_state.scan_mode ? D3DPRESENTFLAG_INTERLACED : D3DPRESENTFLAG_PROGRESSIVE;
-    m_desktopRes.fRefreshRate = (float)tv_state.frame_rate;
-    m_desktopRes.strMode.Format("%dx%d", tv_state.width, tv_state.height);
-    if((float)tv_state.frame_rate > 1)
+    memset(&tv_state, 0, sizeof(TV_DISPLAY_STATE_T));
+    m_DllBcmHost->vc_tv_get_display_state(&tv_state);
+
+    if ((tv_state.state & ( VC_HDMI_HDMI | VC_HDMI_DVI )) != 0) // hdtv
     {
-        m_desktopRes.strMode.Format("%s @ %.2f%s - Full Screen", m_desktopRes.strMode, (float)tv_state.frame_rate,
-            m_desktopRes.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+      m_desktopRes.iScreen      = 0;
+      m_desktopRes.bFullScreen  = true;
+      m_desktopRes.iWidth       = tv_state.display.hdmi.width;
+      m_desktopRes.iHeight      = tv_state.display.hdmi.height;
+      m_desktopRes.iScreenWidth = tv_state.display.hdmi.width;
+      m_desktopRes.iScreenHeight= tv_state.display.hdmi.height;
+      m_desktopRes.dwFlags      = MAKEFLAGS(tv_state.display.hdmi.group, tv_state.display.hdmi.mode, tv_state.display.hdmi.scan_mode);
+      // Also add 3D flags
+      if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_SBS_HALF)
+      {
+        m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
+        m_desktopRes.iWidth       >>= 1;
+        m_desktopRes.iScreenWidth >>= 1;
+      }
+      else if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_TB_HALF)
+      {
+        m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
+        m_desktopRes.iHeight       >>= 1;
+        m_desktopRes.iScreenHeight >>= 1;
+      }
+      m_desktopRes.fRefreshRate = (float)tv_state.display.hdmi.frame_rate;
+      m_desktopRes.fPixelRatio  = get_display_aspect_ratio((HDMI_ASPECT_T)tv_state.display.hdmi.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
+    }
+    else // sdtv
+    {
+      m_desktopRes.iScreen      = 0;
+      m_desktopRes.bFullScreen  = true;
+      m_desktopRes.iWidth       = tv_state.display.sdtv.width;
+      m_desktopRes.iHeight      = tv_state.display.sdtv.height;
+      m_desktopRes.iScreenWidth = tv_state.display.sdtv.width;
+      m_desktopRes.iScreenHeight= tv_state.display.sdtv.height;
+      m_desktopRes.dwFlags      = D3DPRESENTFLAG_INTERLACED;
+      m_desktopRes.fRefreshRate = (float)tv_state.display.sdtv.frame_rate;
+      m_desktopRes.fPixelRatio  = get_display_aspect_ratio((SDTV_ASPECT_T)tv_state.display.sdtv.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
+    }
+
+    m_desktopRes.strMode.Format("%dx%d", m_desktopRes.iScreenWidth, m_desktopRes.iScreenHeight);
+
+    if((int)m_desktopRes.fRefreshRate > 1)
+    {
+      m_desktopRes.strMode.Format("%dx%d @ %.2f%s - Full Screen", m_desktopRes.iScreenWidth, m_desktopRes.iScreenHeight, m_desktopRes.fRefreshRate,
+        m_desktopRes.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
     }
     m_initDesktopRes = false;
 
@@ -381,26 +473,18 @@ bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &r
 
     m_desktopRes.iSubtitles   = (int)(0.965 * m_desktopRes.iHeight);
 
-    CLog::Log(LOGDEBUG, "EGL initial desktop resolution %s\n", m_desktopRes.strMode.c_str());
+    CLog::Log(LOGDEBUG, "EGL initial desktop resolution %s (%.2f)\n", m_desktopRes.strMode.c_str(), m_desktopRes.fPixelRatio);
   }
 
-
   GetSupportedModes(HDMI_RES_GROUP_CEA, resolutions);
   GetSupportedModes(HDMI_RES_GROUP_DMT, resolutions);
-  GetSupportedModes(HDMI_RES_GROUP_CEA_3D, resolutions);
 
   if(resolutions.size() == 0)
   {
-    TV_GET_STATE_RESP_T tv;
-    m_DllBcmHost->vc_tv_get_state(&tv);
-
     RESOLUTION_INFO res;
-    CLog::Log(LOGDEBUG, "EGL probe resolution %dx%d@%f %s:%x\n",
-        m_desktopRes.iWidth, m_desktopRes.iHeight, m_desktopRes.fRefreshRate,
-        m_desktopRes.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "p");
+    CLog::Log(LOGDEBUG, "EGL probe resolution %s:%x\n", m_desktopRes.strMode.c_str(), m_desktopRes.dwFlags);
 
-    m_res.push_back(m_desktopRes);
-    resolutions.push_back(m_desktopRes);
+    AddUniqueResolution(m_desktopRes, resolutions);
   }
 
   if(resolutions.size() < 2)
@@ -458,45 +542,43 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
   if(!m_DllBcmHost)
     return;
 
-  //Supported HDMI CEA/DMT resolutions, first one will be preferred resolution
-  TV_SUPPORTED_MODE_T supported_modes[TV_MAX_SUPPORTED_MODES];
-  int32_t num_modes;
+  //Supported HDMI CEA/DMT resolutions, preferred resolution will be returned
+  int32_t num_modes = 0;
   HDMI_RES_GROUP_T prefer_group;
   uint32_t prefer_mode;
   int i;
+  TV_SUPPORTED_MODE_NEW_T *supported_modes = NULL;
+  // query the number of modes first
+  int max_supported_modes = m_DllBcmHost->vc_tv_hdmi_get_supported_modes_new(group, NULL, 0, &prefer_group, &prefer_mode);
 
-  num_modes = m_DllBcmHost->vc_tv_hdmi_get_supported_modes(group,
-      supported_modes, TV_MAX_SUPPORTED_MODES, &prefer_group, &prefer_mode);
+  if (max_supported_modes > 0)
+    supported_modes = new TV_SUPPORTED_MODE_NEW_T[max_supported_modes];
 
-  CLog::Log(LOGDEBUG, "EGL get supported modes (%d) = %d, prefer_group=%x, prefer_mode=%x\n",
-      group, num_modes, prefer_group, prefer_mode);
+  if (supported_modes)
+  {
+    num_modes = m_DllBcmHost->vc_tv_hdmi_get_supported_modes_new(group,
+        supported_modes, max_supported_modes, &prefer_group, &prefer_mode);
+
+    CLog::Log(LOGDEBUG, "EGL get supported modes (%d) = %d, prefer_group=%x, prefer_mode=%x\n",
+        group, num_modes, prefer_group, prefer_mode);
+  }
 
   if (num_modes > 0 && prefer_group != HDMI_RES_GROUP_INVALID)
   {
-    TV_SUPPORTED_MODE_T *tv = supported_modes;
+    TV_SUPPORTED_MODE_NEW_T *tv = supported_modes;
     for (i=0; i < num_modes; i++, tv++)
     {
-      // treat 3D modes as half-width SBS
-      unsigned int width = (group == HDMI_RES_GROUP_CEA_3D) ? tv->width>>1 : tv->width;
       RESOLUTION_INFO res;
-      CLog::Log(LOGDEBUG, "EGL mode %d: %dx%d@%d %s%s:%x\n", i, width, tv->height, tv->frame_rate,
-          tv->native ? "N" : "", tv->scan_mode ? "I" : "", tv->code);
 
       res.iScreen       = 0;
       res.bFullScreen   = true;
-      res.dwFlags       = MAKEFLAGS(group, tv->code, tv->scan_mode, group==HDMI_RES_GROUP_CEA_3D);
+      res.dwFlags       = MAKEFLAGS(group, tv->code, tv->scan_mode);
       res.fRefreshRate  = (float)tv->frame_rate;
-      res.fPixelRatio   = 1.0f;
-      res.iWidth        = width;
+      res.iWidth        = tv->width;
       res.iHeight       = tv->height;
-      res.iScreenWidth  = width;
+      res.iScreenWidth  = tv->width;
       res.iScreenHeight = tv->height;
-      res.strMode.Format("%dx%d", width, tv->height);
-      if((float)tv->frame_rate > 1)
-      {
-        res.strMode.Format("%dx%d @ %.2f%s - Full Screen", res.iScreenWidth, res.iScreenHeight, res.fRefreshRate,
-          res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
-      }
+      res.fPixelRatio   = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res.iScreenWidth / (float)res.iScreenHeight);
 
       int gui_width  = res.iWidth;
       int gui_height = res.iHeight;
@@ -506,23 +588,53 @@ void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::v
       res.iWidth = gui_width;
       res.iHeight = gui_height;
 
+      res.strMode.Format("%dx%d @ %.2f%s - Full Screen", res.iScreenWidth, res.iScreenHeight, res.fRefreshRate,
+        res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+
+      CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) %s%s:%x\n", i, res.strMode.c_str(), res.fPixelRatio,
+          tv->native ? "N" : "", tv->scan_mode ? "I" : "", tv->code);
+
       res.iSubtitles    = (int)(0.965 * res.iHeight);
 
-      resolutions.push_back(res);
+      AddUniqueResolution(res, resolutions);
 
-      /* replace initial desktop resolution with probed hdmi resolution */
-      if(m_desktopRes.iWidth == res.iWidth && m_desktopRes.iHeight == res.iHeight &&
-          m_desktopRes.iScreenWidth == res.iScreenWidth && m_desktopRes.iScreenHeight == res.iScreenHeight &&
-          m_desktopRes.fRefreshRate == res.fRefreshRate)
+      // Also add 3D versions of modes
+      if (tv->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL)
       {
-        m_desktopRes = res;
-        CLog::Log(LOGDEBUG, "EGL desktop replacement resolution %dx%d@%d %s%s:%x\n",
-            width, tv->height, tv->frame_rate, tv->native ? "N" : "", tv->scan_mode ? "I" : "", tv->code);
+        RESOLUTION_INFO res2 = res;
+        res2.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
+        res2.iWidth       >>= 1;
+        res2.iScreenWidth >>= 1;
+        res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
+        res2.strMode.Format("%dx%d", res2.iScreenWidth, res2.iScreenHeight);
+        res2.strMode.Format("%dx%d @ %.2f%s - Full Screen", res2.iScreenWidth, res2.iScreenHeight, res2.fRefreshRate,
+            res2.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+        CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) SBS\n", i, res2.strMode.c_str(), res2.fPixelRatio);
+
+        res2.iSubtitles    = (int)(0.965 * res2.iHeight);
+
+        AddUniqueResolution(res2, resolutions);
+      }
+      if (tv->struct_3d_mask & HDMI_3D_STRUCT_TOP_AND_BOTTOM)
+      {
+        RESOLUTION_INFO res2 = res;
+        res2.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
+        res2.iHeight       >>= 1;
+        res2.iScreenHeight >>= 1;
+        res2.fPixelRatio    = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
+        res2.strMode.Format("%dx%d", res2.iScreenWidth, res2.iScreenHeight);
+        res2.strMode.Format("%dx%d @ %.2f%s - Full Screen", res2.iScreenWidth, res2.iScreenHeight, res2.fRefreshRate,
+            res2.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
+        CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) TAB\n", i, res2.strMode.c_str(), res2.fPixelRatio);
+
+        res2.iSubtitles    = (int)(0.965 * res2.iHeight);
+
+        AddUniqueResolution(res2, resolutions);
       }
-
-      m_res.push_back(res);
     }
   }
+  if (supported_modes)
+    delete supported_modes;
 }
 
 void CEGLNativeTypeRaspberryPI::TvServiceCallback(uint32_t reason, uint32_t param1, uint32_t param2)
index f54fb58..9af2123 100644 (file)
@@ -61,7 +61,6 @@ private:
   TV_GET_STATE_RESP_T           m_tv_state;
   sem_t                         m_tv_synced;
   bool                          m_fixedMode;
-  std::vector<RESOLUTION_INFO>  m_res;
   RESOLUTION_INFO               m_desktopRes;
   int                           m_width;
   int                           m_height;
@@ -73,5 +72,7 @@ private:
 
   void DestroyDispmaxWindow();
   bool ClampToGUIDisplayLimits(int &width, int &height);
+  int FindMatchingResolution(const RESOLUTION_INFO &res, const std::vector<RESOLUTION_INFO> &resolutions);
+  int AddUniqueResolution(const RESOLUTION_INFO &res, std::vector<RESOLUTION_INFO> &resolutions);
 #endif
 };
index 1698e26..4a0f2bb 100644 (file)
@@ -24,6 +24,7 @@
 #include "WinSystemEGL.h"
 #include "filesystem/SpecialProtocol.h"
 #include "settings/Settings.h"
+#include "settings/GUISettings.h"
 #include "utils/log.h"
 #include "EGLWrapper.h"
 #include "EGLQuirks.h"
@@ -437,33 +438,51 @@ EGLContext CWinSystemEGL::GetEGLContext()
   return m_context;
 }
 
+// the logic in this function should match whether CBaseRenderer::FindClosestResolution picks a 3D mode
 bool CWinSystemEGL::Support3D(int width, int height, uint32_t mode) const
 {
-  bool bFound = false;
-  int searchMode = 0;
-  int searchWidth = width;
-  int searchHeight = height;
+  RESOLUTION_INFO &curr = g_settings.m_ResInfo[g_graphicsContext.GetVideoResolution()];
 
-  if (mode & D3DPRESENTFLAG_MODE3DSBS)
+  // if we are using automatic hdmi mode switching
+  if (g_guiSettings.GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF)
   {
-    searchWidth /= 2;
-    searchMode = D3DPRESENTFLAG_MODE3DSBS;
-  }
-  else if (mode & D3DPRESENTFLAG_MODE3DTB)
-  {
-    searchHeight /= 2;
-    searchMode = D3DPRESENTFLAG_MODE3DTB;
-  }
+    int searchWidth = curr.iScreenWidth;
+    int searchHeight = curr.iScreenHeight;
 
-  for (unsigned int i = 0; i < g_settings.m_ResInfo.size(); i++)
+    // work out current non-3D resolution (in case we are currently in 3D mode)
+    if (curr.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
+    {
+      searchWidth *= 2;
+    }
+    else if (curr.dwFlags & D3DPRESENTFLAG_MODE3DTB)
+    {
+      searchHeight *= 2;
+    }
+    // work out resolution if we switch to 3D mode
+    if (mode & D3DPRESENTFLAG_MODE3DSBS)
+    {
+      searchWidth /= 2;
+    }
+    else if (mode & D3DPRESENTFLAG_MODE3DTB)
+    {
+      searchHeight /= 2;
+    }
+    // only search the custom resolutions
+    for (unsigned int i = (int)RES_DESKTOP; i < g_settings.m_ResInfo.size(); i++)
+    {
+      RESOLUTION_INFO res = g_settings.m_ResInfo[i];
+      if(res.iScreenWidth == searchWidth && res.iScreenHeight == searchHeight && (res.dwFlags & mode))
+        return true;
+    }
+  }
+  // otherwise just consider current mode
+  else
   {
-    RESOLUTION_INFO res = g_settings.m_ResInfo[i];
-
-    if(res.iWidth == searchWidth && res.iHeight == searchHeight && (res.dwFlags & searchMode))
-      return true;
+     if (curr.dwFlags & mode)
+       return true;
   }
 
-  return bFound;
+  return false;
 }
 
 bool CWinSystemEGL::ClampToGUIDisplayLimits(int &width, int &height)
index 8fab131..4fb4619 100644 (file)
@@ -36,6 +36,7 @@
 #include "osx/XBMCHelper.h"
 #include "utils/SystemInfo.h"
 #include "osx/CocoaInterface.h"
+#include "osx/DarwinUtils.h"
 #undef BOOL
 
 #import <SDL/SDL_video.h>
@@ -1299,6 +1300,7 @@ bool CWinSystemOSX::IsObscured(void)
   // default to false before we start parsing though the windows.
   // if we are are obscured by any windows, then set true.
   m_obscured = false;
+  static bool obscureLogged = false;
 
   CGWindowListOption opts;
   opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements;
@@ -1342,7 +1344,8 @@ bool CWinSystemOSX::IsObscured(void)
     if (CFStringCompare(ownerName, CFSTR("Shades"), 0)            == kCFCompareEqualTo ||
         CFStringCompare(ownerName, CFSTR("SmartSaver"), 0)        == kCFCompareEqualTo ||
         CFStringCompare(ownerName, CFSTR("Brightness Slider"), 0) == kCFCompareEqualTo ||
-        CFStringCompare(ownerName, CFSTR("Displaperture"), 0)     == kCFCompareEqualTo)
+        CFStringCompare(ownerName, CFSTR("Displaperture"), 0)     == kCFCompareEqualTo ||
+        CFStringCompare(ownerName, CFSTR("Dreamweaver"), 0)       == kCFCompareEqualTo)
       continue;
 
     CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds);
@@ -1355,6 +1358,13 @@ bool CWinSystemOSX::IsObscured(void)
       if (CGRectContainsRect(windowBounds, bounds))
       {
         // if the windowBounds completely encloses our bounds, we are obscured.
+        if (!obscureLogged)
+        {
+          std::string appName;
+          if (DarwinCFStringRefToString(ownerName, appName))
+            CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window %s obscures XBMC!", appName.c_str());
+          obscureLogged = true;
+        }
         m_obscured = true;
         break;
       }
@@ -1372,6 +1382,10 @@ bool CWinSystemOSX::IsObscured(void)
 
   if (!m_obscured)
   {
+    // if we are here we are not obscured by any fullscreen window - reset flag
+    // for allowing the logmessage above to show again if this changes.
+    if (obscureLogged)
+      obscureLogged = false;
     std::vector<CRect> rects = ourBounds.SubtractRects(partialOverlaps);
     // they got us covered
     if (rects.size() == 0)
@@ -1465,13 +1479,17 @@ void CWinSystemOSX::EnableSystemScreenSaver(bool bEnable)
 
   if (!bEnable)
   {
-    CFStringRef reasonForActivity= CFSTR("XBMC requested disable system screen saver");
-    IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
-      kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
+    if (assertionID == 0)
+    {
+      CFStringRef reasonForActivity= CFSTR("XBMC requested disable system screen saver");
+      IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
+        kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
+    }
   }
   else if (assertionID != 0)
   {
     IOPMAssertionRelease(assertionID);
+    assertionID = 0;
   }
 
   m_use_system_screensaver = bEnable;
index 8774c05..c6383e3 100644 (file)
@@ -464,6 +464,8 @@ LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
           CLog::Log(LOGDEBUG, __FUNCTION__": Focus switched to process %s", procfile.c_str());
       }
       break;
+    /* needs to be reviewed after frodo. we reset the system idle timer
+       and the display timer directly now (see m_screenSaverTimer).
     case WM_SYSCOMMAND:
       switch( wParam&0xFFF0 )
       {
@@ -476,7 +478,7 @@ LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
         case SC_SCREENSAVE:
           return 0;
       }
-      break;
+      break;*/
     case WM_SYSKEYDOWN:
       switch (wParam)
       {