merged: webinterface branch into trunk.
authorjmarshallnz <jmarshallnz@svn>
Fri, 10 Sep 2010 22:53:16 +0000 (22:53 +0000)
committerjmarshallnz <jmarshallnz@svn>
Fri, 10 Sep 2010 22:53:16 +0000 (22:53 +0000)
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@33665 568bbfeb-2a22-0410-94d2-cc84cf5bfa90

29 files changed:
XBMC.xcodeproj/project.pbxproj
addons/webinterface.default/css/core.css
addons/webinterface.default/images/ajax-loader.gif [new file with mode: 0644]
addons/webinterface.default/images/close-button.png [new file with mode: 0644]
addons/webinterface.default/images/header-selected-bg.png [new file with mode: 0644]
addons/webinterface.default/images/logo.png
addons/webinterface.default/images/play-icon.png [new file with mode: 0644]
addons/webinterface.default/images/subheader-selected-bg.png [new file with mode: 0644]
addons/webinterface.default/images/top-fade.png [new file with mode: 0644]
addons/webinterface.default/index.html
addons/webinterface.default/js/Launcher.js
addons/webinterface.default/js/MediaLibrary.js
addons/webinterface.default/js/NowPlayingManager.js
tools/Linux/packaging/debian/rules.hardy
xbmc/ApplicationMessenger.cpp
xbmc/ApplicationMessenger.h
xbmc/DateTime.cpp
xbmc/DateTime.h
xbmc/FileSystem/MusicDatabaseDirectory/DirectoryNodeAlbum.cpp
xbmc/GUIDialogSmartPlaylistRule.cpp
xbmc/MusicDatabase.cpp
xbmc/MusicDatabase.h
xbmc/MusicInfoScanner.cpp
xbmc/PlayListPlayer.cpp
xbmc/PlayListPlayer.h
xbmc/lib/libjsonrpc/AVPlaylistOperations.cpp
xbmc/lib/libjsonrpc/AudioLibrary.cpp
xbmc/lib/libjsonrpc/JSONRPC.cpp
xbmc/utils/WebServer.cpp

index f6a05cd..df56146 100644 (file)
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/bash;
-                       shellScript = "#!/bin/bash\n\necho \"copy root files\"\n\nif [ \"$ACTION\" = build ] ; then\n\n# for external testing\n#TARGET_NAME=XBMC.app\n#SRCROOT=/Users/Shared/xbmc_svn/XBMC\n#TARGET_BUILD_DIR=/Users/Shared/xbmc_svn/XBMC/build/Debug\n\n# Force TARGET_NAME on ppc (do not use XBMC_ppc.app)\nTARGET_NAME=XBMC.app\n\n# rsync command with exclusions for items we don't want in the app package\nSYNC=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude *.dll --exclude *.DLL --exclude *linux.* --exclude *.zlib --exclude *.a\"\n\n# rsync command for excluding pngs and jpgs as well. Note that if the skin itself is not compiled\n# using XBMCTex then excluding the pngs and jpgs will most likely make the skin unusable \nSYNCSKIN=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude *.dll  --exclude *.DLL --exclude *linux.* --exclude *.png --exclude *.jpg --exclude *.bat\"\n\n# rsync command for including everything but the skins\nADDONSYNC=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude skin.confluence\"\n\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/language\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/media\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/sounds\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/system\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/userdata\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/media\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/web\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/tools/osx\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/extras/user\"\n\n${SYNC} \"$SRCROOT/LICENSE.GPL\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/\"\n${SYNC} \"$SRCROOT/xbmc/osx/Credits.html\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/\"\n${SYNC} \"$SRCROOT/tools/osx\"\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/tools\"\n${ADDONSYNC} \"$SRCROOT/addons\"\t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/language\"\t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/media\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNCSKIN} \"$SRCROOT/addons/skin.confluence\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons\"\n${SYNC} \"$SRCROOT/addons/skin.confluence/backgrounds\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons/skin.confluence\"\n${SYNC} \"$SRCROOT/addons/skin.confluence/icon.png\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons/skin.confluence\"\n${SYNC} \"$SRCROOT/sounds\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/system\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/userdata\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/web/poc_jsonrpc/\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/web\"\n\n# copy extra packages if applicable\nif [ -d \"$SRCROOT/extras/system\" ]; then\n\t${SYNC} \"$SRCROOT/extras/system/\" \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\nfi\n\n# copy extra user packages if applicable\nif [ -d \"$SRCROOT/extras/user\" ]; then\n\t${SYNC} \"$SRCROOT/extras/user/\" \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/extras/user\"\nfi\n\n\n\n# magic that gets the icon to update\ntouch \"$TARGET_BUILD_DIR/$TARGET_NAME\"\n\n# not sure we want to do this with out major testing, many scripts cannot handle the spaces in the app name\n#mv \"$TARGET_BUILD_DIR/$TARGET_NAME\" \"$TARGET_BUILD_DIR/XBMC Media Center.app\"\n\nfi";
+                       shellScript = "#!/bin/bash\n\necho \"copy root files\"\n\nif [ \"$ACTION\" = build ] ; then\n\n# for external testing\n#TARGET_NAME=XBMC.app\n#SRCROOT=/Users/Shared/xbmc_svn/XBMC\n#TARGET_BUILD_DIR=/Users/Shared/xbmc_svn/XBMC/build/Debug\n\n# Force TARGET_NAME on ppc (do not use XBMC_ppc.app)\nTARGET_NAME=XBMC.app\n\n# rsync command with exclusions for items we don't want in the app package\nSYNC=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude *.dll --exclude *.DLL --exclude *linux.* --exclude *.zlib --exclude *.a\"\n\n# rsync command for excluding pngs and jpgs as well. Note that if the skin itself is not compiled\n# using XBMCTex then excluding the pngs and jpgs will most likely make the skin unusable \nSYNCSKIN=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude *.dll  --exclude *.DLL --exclude *linux.* --exclude *.png --exclude *.jpg --exclude *.bat\"\n\n# rsync command for including everything but the skins\nADDONSYNC=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude skin.confluence\"\n\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/language\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/media\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/sounds\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/system\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/userdata\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/media\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/web\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/tools/osx\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/extras/user\"\n\n${SYNC} \"$SRCROOT/LICENSE.GPL\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/\"\n${SYNC} \"$SRCROOT/xbmc/osx/Credits.html\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/\"\n${SYNC} \"$SRCROOT/tools/osx\"\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/tools\"\n${ADDONSYNC} \"$SRCROOT/addons\"\t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/language\"\t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/media\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNCSKIN} \"$SRCROOT/addons/skin.confluence\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons\"\n${SYNC} \"$SRCROOT/addons/skin.confluence/backgrounds\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons/skin.confluence\"\n${SYNC} \"$SRCROOT/addons/skin.confluence/icon.png\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons/skin.confluence\"\n${SYNC} \"$SRCROOT/sounds\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/system\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/userdata\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/web/\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/web\"\n\n# copy extra packages if applicable\nif [ -d \"$SRCROOT/extras/system\" ]; then\n\t${SYNC} \"$SRCROOT/extras/system/\" \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\nfi\n\n# copy extra user packages if applicable\nif [ -d \"$SRCROOT/extras/user\" ]; then\n\t${SYNC} \"$SRCROOT/extras/user/\" \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/extras/user\"\nfi\n\n\n\n# magic that gets the icon to update\ntouch \"$TARGET_BUILD_DIR/$TARGET_NAME\"\n\n# not sure we want to do this with out major testing, many scripts cannot handle the spaces in the app name\n#mv \"$TARGET_BUILD_DIR/$TARGET_NAME\" \"$TARGET_BUILD_DIR/XBMC Media Center.app\"\n\nfi";
                };
                F5A1CBDF0F6B0B4700A96ABD /* copy frameworks */ = {
                        isa = PBXShellScriptBuildPhase;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/bash;
-                       shellScript = "#!/bin/bash\n\necho \"copy root files\"\n\nif [ \"$ACTION\" = build ] ; then\n\n# for external testing\n#TARGET_NAME=XBMC.app\n#SRCROOT=/Users/Shared/xbmc_svn/XBMC\n#TARGET_BUILD_DIR=/Users/Shared/xbmc_svn/XBMC/build/Debug\n\n# rsync command with exclusions for items we don't want in the app package\nSYNC=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude *.dll --exclude *.DLL --exclude *linux.* --exclude *.zlib --exclude *.a\"\n\n# rsync command for excluding pngs and jpgs as well. Note that if the skin itself is not compiled\n# using XBMCTex then excluding the pngs and jpgs will most likely make the skin unusable \nSYNCSKIN=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude *.dll  --exclude *.DLL --exclude *linux.* --exclude *.png --exclude *.jpg --exclude *.bat\"\n\n# rsync command for including everything but the skins\nADDONSYNC=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude skin.confluence\"\n\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/language\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/media\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/sounds\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/system\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/userdata\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/media\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/web\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/tools/osx\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/extras/user\"\n\n${SYNC} \"$SRCROOT/LICENSE.GPL\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/\"\n${SYNC} \"$SRCROOT/xbmc/osx/Credits.html\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/\"\n${SYNC} \"$SRCROOT/tools/osx\"\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/tools\"\n${ADDONSYNC} \"$SRCROOT/addons\"\t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/language\"\t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/media\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNCSKIN} \"$SRCROOT/addons/skin.confluence\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons\"\n${SYNC} \"$SRCROOT/addons/skin.confluence/backgrounds\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons/skin.confluence\"\n${SYNC} \"$SRCROOT/addons/skin.confluence/icon.png\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons/skin.confluence\"\n${SYNC} \"$SRCROOT/sounds\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/system\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/userdata\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/web/poc_jsonrpc/\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/web\"\n\n# copy extra packages if applicable\nif [ -d \"$SRCROOT/extras/system\" ]; then\n\t${SYNC} \"$SRCROOT/extras/system/\" \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\nfi\n\n# copy extra user packages if applicable\nif [ -d \"$SRCROOT/extras/user\" ]; then\n\t${SYNC} \"$SRCROOT/extras/user/\" \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/extras/user\"\nfi\n\n\n\n# magic that gets the icon to update\ntouch \"$TARGET_BUILD_DIR/$TARGET_NAME\"\n\n# not sure we want to do this with out major testing, many scripts cannot handle the spaces in the app name\n#mv \"$TARGET_BUILD_DIR/$TARGET_NAME\" \"$TARGET_BUILD_DIR/XBMC Media Center.app\"\n\nfi";
+                       shellScript = "#!/bin/bash\n\necho \"copy root files\"\n\nif [ \"$ACTION\" = build ] ; then\n\n# for external testing\n#TARGET_NAME=XBMC.app\n#SRCROOT=/Users/Shared/xbmc_svn/XBMC\n#TARGET_BUILD_DIR=/Users/Shared/xbmc_svn/XBMC/build/Debug\n\n# rsync command with exclusions for items we don't want in the app package\nSYNC=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude *.dll --exclude *.DLL --exclude *linux.* --exclude *.zlib --exclude *.a\"\n\n# rsync command for excluding pngs and jpgs as well. Note that if the skin itself is not compiled\n# using XBMCTex then excluding the pngs and jpgs will most likely make the skin unusable \nSYNCSKIN=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude *.dll  --exclude *.DLL --exclude *linux.* --exclude *.png --exclude *.jpg --exclude *.bat\"\n\n# rsync command for including everything but the skins\nADDONSYNC=\"rsync -av --exclude CVS* --exclude .svn* --exclude .cvsignore* --exclude .cvspass* --exclude .DS_Store* --exclude skin.confluence\"\n\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/language\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/media\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/sounds\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/system\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/userdata\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/media\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/web\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/tools/osx\"\nmkdir -p \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/extras/user\"\n\n${SYNC} \"$SRCROOT/LICENSE.GPL\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/\"\n${SYNC} \"$SRCROOT/xbmc/osx/Credits.html\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/\"\n${SYNC} \"$SRCROOT/tools/osx\"\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/tools\"\n${ADDONSYNC} \"$SRCROOT/addons\"\t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/language\"\t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/media\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNCSKIN} \"$SRCROOT/addons/skin.confluence\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons\"\n${SYNC} \"$SRCROOT/addons/skin.confluence/backgrounds\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons/skin.confluence\"\n${SYNC} \"$SRCROOT/addons/skin.confluence/icon.png\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/addons/skin.confluence\"\n${SYNC} \"$SRCROOT/sounds\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/system\" \t\t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/userdata\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\n${SYNC} \"$SRCROOT/web/\" \t\"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/web\"\n\n# copy extra packages if applicable\nif [ -d \"$SRCROOT/extras/system\" ]; then\n\t${SYNC} \"$SRCROOT/extras/system/\" \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC\"\nfi\n\n# copy extra user packages if applicable\nif [ -d \"$SRCROOT/extras/user\" ]; then\n\t${SYNC} \"$SRCROOT/extras/user/\" \"$TARGET_BUILD_DIR/$TARGET_NAME/Contents/Resources/XBMC/extras/user\"\nfi\n\n\n\n# magic that gets the icon to update\ntouch \"$TARGET_BUILD_DIR/$TARGET_NAME\"\n\n# not sure we want to do this with out major testing, many scripts cannot handle the spaces in the app name\n#mv \"$TARGET_BUILD_DIR/$TARGET_NAME\" \"$TARGET_BUILD_DIR/XBMC Media Center.app\"\n\nfi";
                        showEnvVarsInLog = 0;
                };
 /* End PBXShellScriptBuildPhase section */
index 7dcbdf2..d276f84 100644 (file)
@@ -6,23 +6,251 @@ body {
 \r
 #header {\r
        position: relative;\r
-       height: 150px;\r
-       background: #333;\r
+       height: 50px;\r
+       border-bottom: 1px solid #000;\r
        }\r
 \r
 #header .logo {\r
+       padding-top: 1px;\r
+       }\r
+\r
+#navigation {\r
+       float: right;\r
+       }\r
+\r
+#spinner {\r
+       float: right;\r
+       padding: 11px 10px;\r
+       }\r
+\r
+#navigation ul {\r
+       list-style-type: none;\r
+       border-right: 1px solid #969696;\r
+       margin: 0;\r
+       padding: 0;\r
+       }\r
+\r
+#navigation ul li {\r
+       float: left;\r
+       color: #000;\r
+       cursor: pointer;\r
+       line-height: 50px;\r
+       margin: 0;\r
+       padding: 0 24px;\r
+       border-left: 1px solid #969696;\r
+       font-family: Verdana, sans-serif;\r
+       font-size: 18px;\r
+       font-weight: 700;\r
+       }\r
+\r
+#navigation ul li.selected,\r
+#navigation ul li:hover {\r
+       background: url('/images/header-selected-bg.png') repeat-x;\r
+       color: #fff;\r
+       }\r
+\r
+.floatableAlbum {\r
+       float: left;\r
+       width: 130px;\r
+       height: 150px;\r
        padding: 10px;\r
-       height: 100px;\r
        }\r
 \r
-#nowPlayingPanel {\r
-       background: #333;\r
-       width: 480px;\r
+.floatableMovieCover {\r
+       float: left;\r
+       width: 130px;\r
+       height: 200px;\r
+       padding: 10px;\r
+       }\r
+\r
+.floatableTVShowCover {\r
+       float: left;\r
+       padding: 10px;\r
+       width: 379px;\r
+       height: 70px;\r
+       }\r
+\r
+#libraryContainer .floatableAlbum,\r
+#movieLibraryContainer .floatableMovieCover,\r
+#tvshowLibraryContainer .floatableTVShowCover {\r
+       cursor: pointer;\r
+       }\r
+\r
+.floatableAlbum div.imgWrapper,\r
+.floatableMovieCover div.imgWrapper,\r
+.floatableTVShowCover div.imgWrapper {\r
+       width: 130px;\r
        height: 130px;\r
+       display: table-cell;\r
+       vertical-align: middle;\r
+       text-align: center;\r
+       }\r
+\r
+div.imgWrapper div.inner {\r
+       overflow: hidden;\r
+       width: 130px;\r
+       }\r
+\r
+.floatableMovieCover div.imgWrapper,\r
+.floatableMovieCover div.imgWrapper div.inner {\r
+       height: 190px;\r
+       }\r
+\r
+#overlay {\r
+       top: 50px;\r
+       left: 0;\r
+       right: 0;\r
+       bottom: 150px;\r
+       background: #3f3f3f;\r
+       position: fixed;\r
+       opacity: 0.8;\r
+       z-index: 2000; /* Above contentContainer's */\r
+       }\r
+\r
+.floatableTVShowCover div.imgWrapper,\r
+.floatableTVShowCover img,\r
+.floatableTVShowCover div.imgWrapper div.inner {\r
+       height: 70px;\r
+       width: 379px;\r
+       }\r
+\r
+.floatableAlbum img {\r
+       width: 130px;\r
+       }\r
+\r
+.floatableMovieCover img {\r
+       height: 180px;\r
+       }\r
+\r
+.floatableAlbum p.album,\r
+.floatableMovieCover p.album {\r
+       font-size: 12px;\r
+       font-weight: 700;\r
+       color: #000;\r
+       text-align: center;\r
+       margin: 0;\r
+       padding: 0;\r
+       width: 130px;\r
+       white-space: nowrap;\r
+       overflow: hidden;\r
+       }\r
+\r
+.floatableAlbum p.artist,\r
+.floatableMovieCover p.artist {\r
+       font-size: 11px;\r
+       color: #777;\r
+       text-align: center;\r
+       margin: 0;\r
+       padding: 0;\r
+       }\r
+\r
+.contentContainer {\r
+       overflow-x: hidden;\r
+       overflow-y: auto;\r
+       position: absolute;\r
+       top: 51px;\r
+       bottom: 0;\r
+       left: 0;\r
+       right: 0;\r
+       background: #fff;\r
+       padding-bottom: 150px;\r
+       }\r
+\r
+.albumView .track {\r
+       cursor: pointer;\r
+       line-height: 10px;\r
+       font-size: 14px;\r
+       padding: 1px 0;\r
+       }\r
+\r
+/* Movie Overlay */\r
+\r
+.moviePopoverContainer {\r
+       z-index: 3000; /* Above overlay */\r
+       border: 1px solid #000;\r
        padding: 10px;\r
+       margin: 10px;\r
+       position: fixed;\r
+       background: #3f3f3f;\r
+       top: 50px;\r
+       bottom: 150px;\r
+       left: 10%;\r
+       right: 10%;\r
+       opacity: 0.9;\r
+       }\r
+\r
+.moviePopoverContainer .closeButton {\r
+       float: right;\r
+       cursor: pointer;\r
+       }\r
+\r
+.moviePopoverContainer .movieCover {\r
+       height: 100%;\r
+       padding-right: 20px;\r
+       float: left;\r
+       z-index: 3100;\r
+       }\r
+\r
+.moviePopoverContainer .movieTitle {\r
+       font-size: 24px;\r
+       font-weight: 700;\r
+       color: #fff;\r
+       margin: 0;\r
+       }\r
+\r
+.moviePopoverContainer .runtime,\r
+.moviePopoverContainer .director,\r
+.moviePopoverContainer .genre,\r
+.moviePopoverContainer .plot {\r
+       color: #fff;\r
+       }\r
+\r
+.movieTitle .year {\r
+       font-weight: 400;\r
+       font-size: 18px;\r
+       }\r
+\r
+.playIcon {\r
+       background: url('/images/play-icon.png') center center no-repeat;\r
        position: absolute;\r
-       top: 0px;\r
-       right: 0px;\r
+       z-index: 3500;\r
+       cursor: pointer;\r
+       opacity: 0.8;\r
+       }\r
+\r
+.playIcon:hover {\r
+       opacity: 1;\r
+       }\r
+\r
+/* Effects */\r
+\r
+#topScrollFade {\r
+       position: fixed;\r
+       top: 51px;\r
+       height: 33px;\r
+       z-index: 101;\r
+       left: 0;\r
+       right: 0;\r
+       background: url('/images/top-fade.png') top left repeat-x;\r
+       }\r
+\r
+/* Now Playing */\r
+\r
+#footerPopover {\r
+       position: fixed;\r
+       height: 150px;\r
+       bottom: 0;\r
+       left: 0;\r
+       right: 0;\r
+       z-index: 10000; /* Top most always */\r
+       background: #333;\r
+       opacity: 0.98;\r
+       }\r
+\r
+#nowPlayingPanel {\r
+       height: 130px;\r
+       width: 480px;\r
+       padding: 10px;\r
        }\r
 \r
 #audioCoverArt img {\r
@@ -119,6 +347,7 @@ body {
        width: 416px;\r
        float: right;\r
        cursor: pointer;\r
+       z-index: 1000;\r
        }\r
 \r
 #nowPlayingPlaylist {\r
@@ -244,91 +473,4 @@ body {
 \r
 #pbNext:hover {\r
        background: url('/images/OSDNextTrackFO.png') no-repeat;\r
-       }\r
-\r
-#navigation {\r
-       position: absolute;\r
-       top: 100px;\r
-       left: 0;\r
-       }\r
-\r
-#navigation ul {\r
-       list-style-type: none;\r
-       }\r
-\r
-#navigation ul li {\r
-       float: left;\r
-       background: #ccc;\r
-       color: #fff;\r
-       cursor: pointer;\r
-       border: 2px solid #333;\r
-       margin: 2px;\r
-       padding: 4px;\r
-       }\r
-\r
-#navigation ul li.selected,\r
-#navigation ul li:hover {\r
-       background: #aaa;\r
-       color: #333;\r
-       }\r
-\r
-.floatableAlbum {\r
-       float: left;\r
-       width: 130px;\r
-       height: 150px;\r
-       padding: 10px;\r
-       }\r
-\r
-#libraryContainer .floatableAlbum {\r
-       cursor: pointer;\r
-       }\r
-\r
-.floatableAlbum div.imgWrapper {\r
-       width: 130px;\r
-       height: 130px;\r
-       display: table-cell;\r
-       vertical-align: middle;\r
-       }\r
-\r
-.floatableAlbum img {\r
-       width: 130px;\r
-       }\r
-\r
-.floatableAlbum p.album {\r
-       font-size: 12px;\r
-       font-weight: 700;\r
-       color: #000;\r
-       text-align: center;\r
-       margin: 0;\r
-       padding: 0;\r
-       width: 130px;\r
-       white-space: nowrap;\r
-       overflow: hidden;\r
-       }\r
-\r
-.floatableAlbum p.artist {\r
-       font-size: 11px;\r
-       color: #777;\r
-       text-align: center;\r
-       margin: 0;\r
-       padding: 0;\r
-       }\r
-\r
-.contentContainer {\r
-       overflow-x: hidden;\r
-       overflow-y: auto;\r
-       position: absolute;\r
-       top: 150px;\r
-       bottom: 0;\r
-       left: 0;\r
-       right: 0;\r
-       background: #fff;\r
-       }\r
-\r
-.albumView .track {\r
-       cursor: pointer;\r
-       line-height: 10px;\r
-       font-size: 14px;\r
-       padding: 1px 0;\r
-       }\r
-\r
+       }
\ No newline at end of file
diff --git a/addons/webinterface.default/images/ajax-loader.gif b/addons/webinterface.default/images/ajax-loader.gif
new file mode 100644 (file)
index 0000000..4fb7c23
Binary files /dev/null and b/addons/webinterface.default/images/ajax-loader.gif differ
diff --git a/addons/webinterface.default/images/close-button.png b/addons/webinterface.default/images/close-button.png
new file mode 100644 (file)
index 0000000..767727a
Binary files /dev/null and b/addons/webinterface.default/images/close-button.png differ
diff --git a/addons/webinterface.default/images/header-selected-bg.png b/addons/webinterface.default/images/header-selected-bg.png
new file mode 100644 (file)
index 0000000..b60d4ae
Binary files /dev/null and b/addons/webinterface.default/images/header-selected-bg.png differ
index 8001cdb..0f1b681 100644 (file)
Binary files a/addons/webinterface.default/images/logo.png and b/addons/webinterface.default/images/logo.png differ
diff --git a/addons/webinterface.default/images/play-icon.png b/addons/webinterface.default/images/play-icon.png
new file mode 100644 (file)
index 0000000..e75fa97
Binary files /dev/null and b/addons/webinterface.default/images/play-icon.png differ
diff --git a/addons/webinterface.default/images/subheader-selected-bg.png b/addons/webinterface.default/images/subheader-selected-bg.png
new file mode 100644 (file)
index 0000000..83725c9
Binary files /dev/null and b/addons/webinterface.default/images/subheader-selected-bg.png differ
diff --git a/addons/webinterface.default/images/top-fade.png b/addons/webinterface.default/images/top-fade.png
new file mode 100644 (file)
index 0000000..21cc82b
Binary files /dev/null and b/addons/webinterface.default/images/top-fade.png differ
index 8dfa157..c4690a3 100644 (file)
@@ -9,13 +9,30 @@
                <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> \r
                <!-- <link href="/images/logo.png" rel="image_src" /> -->\r
                <meta name="robots" content="NOINDEX, NOFOLLOW">\r
-               <link href="/css/core.css" rel="stylesheet" type="text/css">\r
+               <link href="/css/core.css?1.1.0" rel="stylesheet" type="text/css">\r
                <script type="text/javascript" src="/js/jquery-1.4.2.js"></script>\r
                <script type="text/javascript" src="/js/Launcher.js?1.0.0"></script>\r
        </head>\r
        <body>\r
                <div id="header">\r
                        <img src="/images/logo.png" alt="XBMC Logo" class="logo">\r
+                       <div id="navigation">\r
+                               <ul>\r
+                                       <li id="remoteControl">Remote</li>\r
+                                       <li id="movieLibrary">Movies</li>\r
+                                       <li id="tvshowLibrary">TV Shows</li>\r
+                                       <li id="musicLibrary">Music</li>\r
+                                       <li id="settingsPanel">Settings</li>\r
+                               </ul>\r
+                       </div>\r
+                       <img src="/images/ajax-loader.gif" alt="Loading please wait" id="spinner" style="display: none">\r
+               </div>\r
+               <div id="body">\r
+                       <div id="topScrollFade" style="display: none;"></div>\r
+                       <div id="content"></div>\r
+                       <div id="overlay" style="display: none;"></div>\r
+               </div>\r
+               <div id="footerPopover">\r
                        <div id="nowPlayingPanel" style="display: none;">\r
                                <div id="nowPlayingContent">\r
                                        <div id="audioDescription">\r
                                        </div>\r
                                </div>\r
                                <span id="nextText">Next:</span>\r
-                               <div id="nextTrack" style="display: none;">\r
-                                       \r
-                               </div>\r
-                               <div id="nowPlayingPlaylist" style="display: none;">\r
-\r
-                               </div>\r
-                       </div>\r
-               </div>\r
-               <div id="body">\r
-                       <div id="navigation">\r
-                               <ul>\r
-                                       <li id="musicLibrary">Music</li>\r
-                                       <li id="videoLibrary">Videos</li>\r
-                               </ul>\r
-                       </div>\r
-                       <div id="content">\r
-\r
+                               <div id="nextTrack" style="display: none;"></div>\r
+                               <div id="nowPlayingPlaylist" style="display: none;"></div>\r
                        </div>\r
                </div>\r
                <script type="text/javascript">\r
index 07a984c..9751704 100644 (file)
 //\r
 // For details, see the script.aculo.us web site: http://script.aculo.us/\r
 \r
+var DEBUG_MODE = true; /* Set to false to enable cached javascript */\r
+\r
 var Launcher = {\r
        VERSION: '1.0.0',\r
        REQUIRED_JQUERY: '1.4.2',\r
        load: function(libraryName) {\r
-               document.write('<script type="text/javascript" src="' + libraryName + '?' + this.VERSION + '"><\/script>');\r
+               document.write('<script type="text/javascript" src="' + libraryName + '?' + (DEBUG_MODE ? this.randomValue() : this.VERSION) + '"><\/script>');\r
        },\r
        init: function() {\r
-       function convertVersionString(versionString) {\r
-               var v = versionString.replace(/_.*|\./g, '');\r
-               v = parseInt(v + (v.length == 4 ? '' : '0'));\r
-               return versionString.indexOf('_') > -1 ? v-1 : v;\r
-       }\r
+               function convertVersionString(versionString) {\r
+                       var v = versionString.replace(/_.*|\./g, '');\r
+                       v = parseInt(v + (v.length == 4 ? '' : '0'));\r
+                       return versionString.indexOf('_') > -1 ? v-1 : v;\r
+               }\r
 \r
-       if(!jQuery || (convertVersionString(jQuery.fn.jquery) < convertVersionString(Launcher.REQUIRED_JQUERY)))\r
-               throw("XBMC Web Interface requires the jQuery JavaScript framework >= " + Launcher.REQUIRED_JQUERY);\r
+               if(!jQuery || (convertVersionString(jQuery.fn.jquery) < convertVersionString(Launcher.REQUIRED_JQUERY)))\r
+                       throw("XBMC Web Interface requires the jQuery JavaScript framework >= " + Launcher.REQUIRED_JQUERY);\r
 \r
-       var js = /Launcher\.js(\?.*)?$/;\r
-       $('html').find('script[src]').each(\r
-               function(i, s) { \r
-                       if (s.src.match(js)) {\r
-                               var path = s.src.replace(js, ''),\r
-                               includes = s.src.match(/\?.*load=([a-z,]*)/);\r
-                               $.each((includes ? includes[1] : 'jquery.lazyload,Core,MediaLibrary,NowPlayingManager').split(','), function(i, include) { \r
-                                       Launcher.load(path + include + '.js') \r
-                               });\r
-                       }\r
-               });\r
+               var js = /Launcher\.js(\?.*)?$/;\r
+               $('html').find('script[src]').each(\r
+                       function(i, s) { \r
+                               if (s.src.match(js)) {\r
+                                       var path = s.src.replace(js, ''),\r
+                                       includes = s.src.match(/\?.*load=([a-z,]*)/);\r
+                                       $.each((includes ? includes[1] : 'jquery.lazyload,Core,MediaLibrary,NowPlayingManager').split(','), function(i, include) { \r
+                                               Launcher.load(path + include + '.js') \r
+                                       });\r
+                               }\r
+                       });\r
+       },\r
+       randomValue: function() {\r
+               return Math.random();\r
        }\r
 }\r
 \r
index c3375b2..20e0e0a 100644 (file)
@@ -30,14 +30,24 @@ MediaLibrary.prototype = {
                },\r
                bindControls: function() {\r
                        $('#musicLibrary').click(jQuery.proxy(this.musicLibraryOpen, this));\r
-                       $('#videoLibrary').click(jQuery.proxy(this.videoLibraryOpen, this));\r
+                       $('#movieLibrary').click(jQuery.proxy(this.movieLibraryOpen, this));\r
+                       $('#tvshowLibrary').click(jQuery.proxy(this.tvshowLibraryOpen, this));\r
+                       $('#overlay').click(jQuery.proxy(this.hideOverlay, this));\r
+                       $(window).resize(jQuery.proxy(this.updatePlayButtonLocation, this));\r
+               },\r
+               resetPage: function() {\r
+                       $('#musicLibrary').removeClass('selected');\r
+                       $('#movieLibrary').removeClass('selected');\r
+                       $('#tvshowLibrary').removeClass('selected');\r
+                       this.hideOverlay();\r
                },\r
                musicLibraryOpen: function(event) {\r
+                       this.resetPage();\r
                        $('#musicLibrary').addClass('selected');\r
-                       $('#videoLibrary').removeClass('selected');\r
                        $('.contentContainer').css('z-index', 1);\r
                        var libraryContainer = $('#libraryContainer');\r
                        if (!libraryContainer || libraryContainer.length == 0) {\r
+                               $('#spinner').show();\r
                                jQuery.post(JSON_RPC + '?GetAlbums', '{"jsonrpc": "2.0", "method": "AudioLibrary.GetAlbums", "params": { "start": 0, "fields": ["album_description", "album_theme", "album_mood", "album_style", "album_type", "album_label", "album_artist", "album_genre", "album_rating", "album_title"] }, "id": 1}', jQuery.proxy(function(data) {\r
                                        if (data && data.result && data.result.albums) {\r
                                                        libraryContainer = $('<div>');\r
@@ -49,19 +59,26 @@ MediaLibrary.prototype = {
                                                libraryContainer.html('');\r
                                        }\r
                                        $.each($(data.result.albums), jQuery.proxy(function(i, item) {\r
-                                               var floatableAlbum = this.generateAlbumThumb(item.thumbnail, item.album_title, item.album_artist);\r
-                                               floatableAlbum.bind('click', {album: item, }, jQuery.proxy(this.displayAlbumDetails, this));\r
+                                               var floatableAlbum = this.generateThumb('album', item.thumbnail, item.album_title, item.album_artist);\r
+                                               floatableAlbum.bind('click', { album: item }, jQuery.proxy(this.displayAlbumDetails, this));\r
                                                libraryContainer.append(floatableAlbum);\r
                                        }, this));\r
+                                       $('#spinner').hide();\r
                                        //$('#libraryContainer img').lazyload();\r
+                                       libraryContainer.bind('scroll', { activeLibrary: libraryContainer }, jQuery.proxy(this.updateScrollEffects, this));\r
+                                       libraryContainer.trigger('scroll');\r
                                }, this), 'json');\r
                        } else {\r
                                libraryContainer.css('z-index', 100);\r
+                               libraryContainer.trigger('scroll');\r
                        }\r
                },\r
-               generateAlbumThumb: function(thumbnail, album_title, album_artist) {\r
+               getThumbnailPath: function(thumbnail) {\r
+                       return thumbnail ? ('/vfs/' + thumbnail) : DEFAULT_ALBUM_COVER;\r
+               },\r
+               generateThumb: function(type, thumbnail, album_title, album_artist) {\r
                        var floatableAlbum = $('<div>');\r
-                       var path = thumbnail ? ('/vfs/' + thumbnail) : DEFAULT_ALBUM_COVER;\r
+                       var path = this.getThumbnailPath(thumbnail);\r
                        var title = album_title;\r
                        var artist = album_artist;\r
                        if (title.length > 18 && !(title.length <= 21)) {\r
@@ -70,14 +87,29 @@ MediaLibrary.prototype = {
                        if (artist.length > 20 && !(artist.length <= 22)) {\r
                                artist = album_artist.substring(0, 20) + '...';\r
                        }\r
-                       floatableAlbum.addClass('floatableAlbum')\r
-                                                 .html('<div class="imgWrapper"><img src="' + path + '" alt="" /></div><p class="album" title="' + album_title + '">' + title + '</p><p class="artist" title="' + album_artist + '">' + artist + '</p>');\r
-                       return floatableAlbum\r
+                       var className = '';\r
+                       var code = '';\r
+                       switch(type) {\r
+                               case 'album':\r
+                                       className = 'floatableAlbum';\r
+                                       code = '<p class="album" title="' + album_title + '">' + title + '</p><p class="artist" title="' + album_artist + '">' + artist + '</p>';\r
+                                       break;\r
+                               case 'movie':\r
+                                       className = 'floatableMovieCover';\r
+                                       code = '<p class="album" title="' + album_title + '">' + title + '</p>';\r
+                                       break;\r
+                               case 'tvshow':\r
+                                       className = 'floatableTVShowCover';\r
+                                       break;\r
+                       }\r
+                       floatableAlbum.addClass(className).html('<div class="imgWrapper"><div class="inner"><img src="' + path + '" alt="" /></div></div>' + code);\r
+                       return floatableAlbum;\r
                },\r
                displayAlbumDetails: function(event) {\r
-                       \r
                        var albumDetailsContainer = $('#albumDetails' + event.data.album.albumid);\r
+                       $('#topScrollFade').hide();\r
                        if (!albumDetailsContainer || albumDetailsContainer.length == 0) {\r
+                               $('#spinner').show();\r
                                jQuery.post(JSON_RPC + '?GetSongs', '{"jsonrpc": "2.0", "method": "AudioLibrary.GetSongs", "params": { "fields": ["title", "artist", "genre", "tracknumber", "discnumber", "duration", "year"], "albumid" : ' + event.data.album.albumid + ' }, "id": 1}', jQuery.proxy(function(data) {\r
                                        albumDetailsContainer = $('<div>');\r
                                        albumDetailsContainer.attr('id', 'albumDetails' + event.data.album.albumid)\r
@@ -97,19 +129,19 @@ MediaLibrary.prototype = {
                                                        trackRow.append(albumTD);\r
                                                }\r
                                                var trackNumberTD = $('<td>');\r
-                                               trackNumberTD.html(item.tracknumber).addClass('track').bind('click', { song: item }, jQuery.proxy(this.playTrack, this));\r
+                                               trackNumberTD.html(item.tracknumber).addClass('track').bind('click', { song: item, album: event.data.album }, jQuery.proxy(this.playTrack, this));\r
                                                trackRow.append(trackNumberTD);\r
                                                var trackTitleTD = $('<td>');\r
-                                               trackTitleTD.html(item.title).addClass('track').bind('click', { song: item }, jQuery.proxy(this.playTrack, this));\r
+                                               trackTitleTD.html(item.title).addClass('track').bind('click', { song: item, album: event.data.album }, jQuery.proxy(this.playTrack, this));\r
                                                trackRow.append(trackTitleTD);\r
                                                var trackDurationTD = $('<td>');\r
-                                               trackDurationTD.html(durationToString(item.duration)).addClass('track').bind('click', { song: item }, jQuery.proxy(this.playTrack, this));\r
+                                               trackDurationTD.html(durationToString(item.duration)).addClass('track').bind('click', { song: item, album: event.data.album }, jQuery.proxy(this.playTrack, this));\r
                                                trackRow.append(trackDurationTD);\r
                                                var trackArtistTD = $('<td>');\r
-                                               trackArtistTD.html(item.artist).addClass('track').bind('click', { song: item }, jQuery.proxy(this.playTrack, this));\r
+                                               trackArtistTD.html(item.artist).addClass('track').bind('click', { song: item, album: event.data.album }, jQuery.proxy(this.playTrack, this));\r
                                                trackRow.append(trackArtistTD);\r
                                                var trackGenreTD = $('<td>');\r
-                                               trackGenreTD.html(item.genre).addClass('track').bind('click', { song: item }, jQuery.proxy(this.playTrack, this));\r
+                                               trackGenreTD.html(item.genre).addClass('track').bind('click', { song: item, album: event.data.album }, jQuery.proxy(this.playTrack, this));\r
                                                trackRow.append(trackGenreTD);\r
                                                $('#albumDetails' + event.data.album.albumid + ' .resultSet').append(trackRow);\r
                                        }, this));\r
@@ -132,22 +164,167 @@ MediaLibrary.prototype = {
                                                trackRow.append(trackGenreTD);\r
                                                $('#albumDetails' + event.data.album.albumid + ' .resultSet').append(trackRow);\r
                                        }\r
-                                       $('#albumDetails' + event.data.album.albumid + ' .albumThumb').append(this.generateAlbumThumb(albumThumbnail, albumTitle, albumArtist));\r
+                                       $('#albumDetails' + event.data.album.albumid + ' .albumThumb').append(this.generateThumb('album', albumThumbnail, albumTitle, albumArtist));\r
                                        $('.contentContainer').css('z-index', 1);\r
+                                       $('#spinner').hide();\r
                                }, this), 'json');\r
                        } else {\r
                                $('.contentContainer').css('z-index', 1);\r
                                $('#albumDetails' + event.data.album.albumid).css('z-index', 100);\r
                        }\r
                },\r
-               playTrack: function(event) {\r
-                       jQuery.post(JSON_RPC + '?PlaySong', '{"jsonrpc": "2.0", "method": "XBMC.Play", "params": { "songid": ' + event.data.song.songid + ' }, "id": 1}', jQuery.proxy(function(data) {\r
+               hideOverlay: function(event) {\r
+                       if (this.activeCover) {\r
+                               $(this.activeCover).remove();\r
+                               this.activeCover = null;\r
+                       }\r
+                       $('#overlay').hide();\r
+               },\r
+               updatePlayButtonLocation: function(event) {\r
+                       var movieContainer = $('.movieCover');\r
+                       if (movieContainer.length > 0) {\r
+                               var playIcon = $('.playIcon');\r
+                               if (playIcon.length > 0) {\r
+                                       playIcon.width($(movieContainer[0]).width());\r
+                                       playIcon.height($(movieContainer[0]).height());\r
+                               }\r
+                       }\r
+               },\r
+               playMovie: function(event) {\r
+                       jQuery.post(JSON_RPC + '?PlayMovie', '{"jsonrpc": "2.0", "method": "XBMC.Play", "params": { "movieid": ' + event.data.movie.movieid + ' }, "id": 1}', jQuery.proxy(function(data) {\r
+                               \r
+                               this.hideOverlay();\r
+                       }, this), 'json');\r
+               },\r
+               displayMovieDetails: function(event) {\r
+                       var movieDetails = $('<div>');\r
+                       movieDetails.attr('id', 'movie-' + event.data.movie.movieid);\r
+                       movieDetails.addClass('moviePopoverContainer');\r
+                       var closeButton = $('<img>');\r
+                       closeButton.attr('src', '/images/close-button.png');\r
+                       closeButton.addClass('closeButton').bind('click', jQuery.proxy(this.hideOverlay, this));\r
+                       movieDetails.append(closeButton);\r
+                       var movieCover = $('<img>');\r
+                       movieCover.attr('src', this.getThumbnailPath(event.data.movie.thumbnail)).addClass('movieCover');\r
+                       movieDetails.append(movieCover);\r
+                       var playIcon = $('<div>');\r
+                       playIcon.addClass('playIcon');\r
+                       playIcon.bind('click', {movie: event.data.movie}, jQuery.proxy(this.playMovie, this));\r
+                       movieDetails.append(playIcon);\r
+                       var movieTitle = $('<p>');\r
+                       movieTitle.addClass('movieTitle');\r
+                       var yearText = event.data.movie.year ? ' <span class="year">(' + event.data.movie.year + ')</span>' : '';\r
+                       movieTitle.html(event.data.movie.title + yearText);\r
+                       movieDetails.append(movieTitle);\r
+                       if (event.data.movie.runtime) {\r
+                               var runtime = $('<p>');\r
+                               runtime.addClass('runtime').html('<strong>Runtime:</strong> ' + event.data.movie.runtime + ' minutes');\r
+                               movieDetails.append(runtime);\r
+                       }\r
+                       if (event.data.movie.plot) {\r
+                               var plot = $('<p>');\r
+                               plot.addClass('plot').html(event.data.movie.plot);\r
+                               movieDetails.append(plot);\r
+                       }\r
+                       if (event.data.movie.genre) {\r
+                               var genre = $('<p>');\r
+                               genre.addClass('genre').html('<strong>Genre:</strong> ' + event.data.movie.genre);\r
+                               movieDetails.append(genre);\r
+                       }\r
+                       if (event.data.movie.rating) {\r
+                               //Todo\r
+                       }\r
+                       if (event.data.movie.director) {\r
+                               var director = $('<p>');\r
+                               director.addClass('director').html('<strong>Directed By:</strong> ' + event.data.movie.director);\r
+                               movieDetails.append(director);\r
+                       }\r
+                       this.activeCover = movieDetails;\r
+                       $('body').append(movieDetails);\r
+                       $('#overlay').show();\r
+                       this.updatePlayButtonLocation();\r
+               },\r
+               displayTVShowDetails: function(event) {\r
 \r
+               },\r
+               playTrack: function(event) {\r
+                       jQuery.post(JSON_RPC + '?ClearPlaylist', '{"jsonrpc": "2.0", "method": "AudioPlaylist.Clear", "id": 1}', jQuery.proxy(function(data) {\r
+                               //check that clear worked.\r
+                               jQuery.post(JSON_RPC + '?AddAlbumToPlaylist', '{"jsonrpc": "2.0", "method": "AudioPlaylist.Add", "params": { "albumid": ' + event.data.album.albumid + ' }, "id": 1}', jQuery.proxy(function(data) {\r
+                                       //play specific song in playlist\r
+                                       jQuery.post(JSON_RPC + '?PlaylistItemPlay', '{"jsonrpc": "2.0", "method": "AudioPlaylist.Play", "params": { "songid": ' + event.data.song.songid + ' }, "id": 1}', function() {}, 'json');\r
+                               }, this), 'json');\r
                        }, this), 'json');\r
                },\r
-               videoLibraryOpen: function() {\r
-                       $('#musicLibrary').removeClass('selected');\r
-                       $('#videoLibrary').addClass('selected');\r
-                       $('#content').html('');\r
+               movieLibraryOpen: function() {\r
+                       this.resetPage();\r
+                       $('#movieLibrary').addClass('selected');\r
+                       $('.contentContainer').css('z-index', 1);\r
+                       var libraryContainer = $('#movieLibraryContainer');\r
+                       if (!libraryContainer || libraryContainer.length == 0) {\r
+                               $('#spinner').show();\r
+                               jQuery.post(JSON_RPC + '?GetMovies', '{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "start": 0, "fields": ["genre", "director", "trailer", "tagline", "plot", "plotoutline", "title", "originaltitle", "lastplayed", "showtitle", "firstaired", "duration", "season", "episode", "runtime", "year", "playcount", "rating"] }, "id": 1}', jQuery.proxy(function(data) {\r
+                                       if (data && data.result && data.result.movies) {\r
+                                                       libraryContainer = $('<div>');\r
+                                                       libraryContainer.css('z-index', 100)\r
+                                                                                       .attr('id', 'movieLibraryContainer')\r
+                                                                                       .addClass('contentContainer');\r
+                                                       $('#content').append(libraryContainer);\r
+                                       } else {\r
+                                               libraryContainer.html('');\r
+                                       }\r
+                                       $.each($(data.result.movies), jQuery.proxy(function(i, item) {\r
+                                               var floatableMovieCover = this.generateThumb('movie', item.thumbnail, item.title, "");\r
+                                               floatableMovieCover.bind('click', { movie: item }, jQuery.proxy(this.displayMovieDetails, this));\r
+                                               libraryContainer.append(floatableMovieCover);\r
+                                       }, this));\r
+                                       $('#spinner').hide();\r
+                                       libraryContainer.bind('scroll', { activeLibrary: libraryContainer }, jQuery.proxy(this.updateScrollEffects, this));\r
+                                       libraryContainer.trigger('scroll');\r
+                                       //$('#libraryContainer img').lazyload();\r
+                               }, this), 'json');\r
+                       } else {\r
+                               libraryContainer.css('z-index', 100);\r
+                               libraryContainer.trigger('scroll');\r
+                       }\r
+               },\r
+               tvshowLibraryOpen: function() {\r
+                       this.resetPage();\r
+                       $('#tvshowLibrary').addClass('selected');\r
+                       $('.contentContainer').css('z-index', 1);\r
+                       var libraryContainer = $('#tvshowLibraryContainer');\r
+                       if (!libraryContainer || libraryContainer.length == 0) {\r
+                               $('#spinner').show();\r
+                               jQuery.post(JSON_RPC + '?GetTVShows', '{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": { "start": 0, "fields": ["genre", "director", "trailer", "tagline", "plot", "plotoutline", "title", "originaltitle", "lastplayed", "showtitle", "firstaired", "duration", "season", "episode", "runtime", "year", "playcount", "rating"] }, "id": 1}', jQuery.proxy(function(data) {\r
+                                       if (data && data.result && data.result.tvshows) {\r
+                                                       libraryContainer = $('<div>');\r
+                                                       libraryContainer.css('z-index', 100)\r
+                                                                                       .attr('id', 'tvshowLibraryContainer')\r
+                                                                                       .addClass('contentContainer');\r
+                                                       $('#content').append(libraryContainer);\r
+                                       } else {\r
+                                               libraryContainer.html('');\r
+                                       }\r
+                                       $.each($(data.result.tvshows), jQuery.proxy(function(i, item) {\r
+                                               var floatableTVShowCover = this.generateThumb('tvshow', item.thumbnail, item.title, "");\r
+                                               floatableTVShowCover.bind('click', { tvshow: item }, jQuery.proxy(this.displayTVShowDetails, this));\r
+                                               libraryContainer.append(floatableTVShowCover);\r
+                                       }, this));\r
+                                       //$('#libraryContainer img').lazyload();\r
+                                       $('#spinner').hide();\r
+                                       libraryContainer.bind('scroll', { activeLibrary: libraryContainer }, jQuery.proxy(this.updateScrollEffects, this));\r
+                                       libraryContainer.trigger('scroll');\r
+                               }, this), 'json');\r
+                       } else {\r
+                               libraryContainer.css('z-index', 100);\r
+                               libraryContainer.trigger('scroll');\r
+                       }\r
+               },\r
+               updateScrollEffects: function(event) {\r
+                       if (event.data.activeLibrary && $(event.data.activeLibrary).scrollTop() > 0) {\r
+                               $('#topScrollFade').fadeIn();\r
+                       } else {\r
+                               $('#topScrollFade').fadeOut();\r
+                       }\r
                }\r
        }
\ No newline at end of file
index d608bd8..29278ec 100644 (file)
@@ -35,7 +35,7 @@ NowPlayingManager.prototype = {
                        $(window).bind('click', jQuery.proxy(this.hidePlaylist, this));\r
                },\r
                updateState: function() {\r
-                       jQuery.post(JSON_RPC, '{"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1}', jQuery.proxy(function(data) {\r
+                       jQuery.post(JSON_RPC + '?UpdateState', '{"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1}', jQuery.proxy(function(data) {\r
                                if (data && data.result) {\r
                                        if (data.result.audio) {\r
                                                this.activePlayer = 'Audio';\r
@@ -166,8 +166,8 @@ NowPlayingManager.prototype = {
                                data: '{"jsonrpc": "2.0", "method": "AudioPlaylist.GetItems", "params": { "fields": ["title", "album", "artist", "duration"] }, "id": 1}', \r
                                success: jQuery.proxy(function(data) {\r
                                        if (data && data.result && data.result.items && data.result.total > 0) {\r
-                                               //Compare new playlist to active playlist, only redraw if a change is noticed.\r
-                                               if (this.playlistChanged(data.result.items) || (this.activePlaylistItem && (this.activePlaylistItem.seq != data.result.current))) {\r
+                                               //Compare new playlist to active playlist, only redraw if a change is noticed\r
+                                               if (!this.activePlaylistItem || this.playlistChanged(data.result.items) || (this.activePlaylistItem && (this.activePlaylistItem.seq != data.result.current))) {\r
                                                        var ul = $('<ul>');\r
                                                        var activeItem;\r
                                                        $.each($(data.result.items), jQuery.proxy(function(i, item) {\r
@@ -179,16 +179,17 @@ NowPlayingManager.prototype = {
                                                                        li.addClass('activeItem');\r
                                                                }\r
                                                                if (i == (data.result.current + 1)) {\r
-                                                                       $('#nextTrack').html(code);\r
-                                                                       $('#nextTrack').show();\r
+                                                                       $('#nextTrack').html(code).show();\r
                                                                }\r
                                                                li.bind('click', jQuery.proxy(this.playPlaylistItem, this));\r
                                                                ul.append(li.attr('seq', i).html(code));\r
                                                        }, this));\r
                                                        if (data.result.total > 1) {\r
+                                                               if (activeItem && data.result.total-1 == activeItem.seq) {\r
+                                                                       $('#nextTrack').html('<div class="trackInfo">Last track in playlist</div>').show();\r
+                                                               }\r
                                                                $('#nextText').show();\r
-                                                               $('#nowPlayingPlaylist').html('')\r
-                                                                                                               .append(ul);\r
+                                                               $('#nowPlayingPlaylist').html('').append(ul);\r
                                                        } else {\r
                                                                $('#nextText').hide();\r
                                                                $('#nowPlayingPlaylist').hide();\r
@@ -226,12 +227,10 @@ NowPlayingManager.prototype = {
                        });\r
                },\r
                stopAudioPlaylistUpdate: function() {\r
-                       this.stopRefreshTime();\r
                        this.autoRefreshAudioPlaylist = false;\r
                        this.updateActiveItemDurationRunOnce = false;\r
                },\r
                stopVideoPlaylistUpdate: function() {\r
-                       this.stopRefreshTime();\r
                        this.autoRefreshVideoPlaylist = false;\r
                        this.updateActiveItemDurationRunOnce = false;\r
                },\r
@@ -255,10 +254,10 @@ NowPlayingManager.prototype = {
                                                        this.refreshVideoData();\r
                                                }\r
                                        }\r
-                                       if (this.autoRefreshData && !this.activeItemTimer) {\r
-                                               this.activeItemTimer = 1;\r
-                                               setTimeout(jQuery.proxy(this.updateActiveItemDurationLoop, this), 1000);\r
-                                       }\r
+                               }\r
+                               if (this.autoRefreshData && !this.activeItemTimer) {\r
+                                       this.activeItemTimer = 1;\r
+                                       setTimeout(jQuery.proxy(this.updateActiveItemDurationLoop, this), 1000);\r
                                }\r
                        }, this), 'json');\r
                },\r
@@ -409,15 +408,17 @@ NowPlayingManager.prototype = {
                                                                        li.addClass('activeItem');\r
                                                                }\r
                                                                if (i == (data.result.current + 1)) {\r
-                                                                       $('#nextTrack').html(code);\r
+                                                                       $('#nextTrack').html(code).show();\r
                                                                }\r
                                                                li.bind('click', jQuery.proxy(this.playPlaylistItem, this));\r
                                                                ul.append(li.attr('seq', i).html(code));\r
                                                        }, this));\r
                                                        if (data.result.total > 1) {\r
                                                                $('#nextText').show();\r
-                                                               $('#nowPlayingPlaylist').html('')\r
-                                                                                                               .append(ul);\r
+                                                               if (activeItem && data.result.total == activeItem.seq) {\r
+                                                                       $('#nextTrack').html('<div class="trackInfo">Last track in playlist</div>').show();\r
+                                                               }\r
+                                                               $('#nowPlayingPlaylist').html('').append(ul);\r
                                                        } else {\r
                                                                $('#nextText').hide();\r
                                                                $('#nowPlayingPlaylist').hide();\r
index 74ca818..fbb7a5c 100755 (executable)
@@ -45,7 +45,7 @@ binary: binary-arch binary-indep
 
 binary-indep:
        dh binary-indep --until dh_installdirs 
-       $(MAKE) install-web install-datas install-livedatas prefix=$(CURDIR)/debian/tmp/usr/
+       $(MAKE) install-datas install-livedatas prefix=$(CURDIR)/debian/tmp/usr/
        mkdir -p $(CURDIR)/debian/tmp/usr/share/applications $(CURDIR)/debian/tmp/usr/share/pixmaps
        cp $(CURDIR)/tools/Linux/xbmc.png $(CURDIR)/debian/tmp/usr/share/pixmaps/
        cp $(CURDIR)/tools/Linux/xbmc.desktop $(CURDIR)/debian/tmp/usr/share/applications/
index 0b87b85..3020102 100644 (file)
@@ -471,7 +471,13 @@ case TMSG_POWERDOWN:
         g_playlistPlayer.Play(pMsg->dwParam1);
       else
         g_playlistPlayer.Play();
+      break;
 
+    case TMSG_PLAYLISTPLAYER_PLAY_SONG_ID:
+      if (pMsg->dwParam1 != (DWORD) -1)
+        g_playlistPlayer.PlaySongId(pMsg->dwParam1);
+      else
+        g_playlistPlayer.Play();
       break;
 
     case TMSG_PLAYLISTPLAYER_NEXT:
@@ -763,6 +769,12 @@ void CApplicationMessenger::PlayListPlayerPlay(int iSong)
   SendMessage(tMsg, true);
 }
 
+void CApplicationMessenger::PlayListPlayerPlaySongId(int songId)
+{
+  ThreadMessage tMsg = {TMSG_PLAYLISTPLAYER_PLAY_SONG_ID, songId};
+  SendMessage(tMsg, true);
+}
+
 void CApplicationMessenger::PlayListPlayerNext()
 {
   ThreadMessage tMsg = {TMSG_PLAYLISTPLAYER_NEXT};
index 8f10eeb..cd0ea75 100644 (file)
@@ -49,6 +49,7 @@ class CGUIDialog;
 #define TMSG_PLAYLISTPLAYER_CLEAR 214
 #define TMSG_PLAYLISTPLAYER_SHUFFLE   215
 #define TMSG_PLAYLISTPLAYER_GET_ITEMS 216
+#define TMSG_PLAYLISTPLAYER_PLAY_SONG_ID 217
 
 #define TMSG_PICTURE_SHOW         220
 #define TMSG_PICTURE_SLIDESHOW    221
@@ -117,6 +118,7 @@ public:
 
   void PlayListPlayerPlay();
   void PlayListPlayerPlay(int iSong);
+  void PlayListPlayerPlaySongId(int songId);
   void PlayListPlayerNext();
   void PlayListPlayerPrevious();
   void PlayListPlayerAdd(int playlist, const CFileItem &item);
index eb499c5..5be49c3 100644 (file)
@@ -1208,3 +1208,35 @@ CStdString CDateTime::GetAsLocalizedDateTime(bool longDate/*=false*/, bool withS
 {
   return GetAsLocalizedDate(longDate)+" "+GetAsLocalizedTime("", withSeconds);
 }
+
+CDateTime CDateTime::GetAsUTCDateTime() const
+{
+  TIME_ZONE_INFORMATION tz;
+
+  CDateTime time(m_time);
+  switch(GetTimeZoneInformation(&tz))
+  {
+    case TIME_ZONE_ID_DAYLIGHT:
+        time += CDateTimeSpan(0, 0, tz.Bias + tz.DaylightBias, 0);
+        break;
+    case TIME_ZONE_ID_STANDARD:
+        time += CDateTimeSpan(0, 0, tz.Bias + tz.StandardBias, 0);
+        break;
+    case TIME_ZONE_ID_UNKNOWN:
+        time += CDateTimeSpan(0, 0, tz.Bias, 0);
+        break;
+  }
+
+  return time;
+}
+
+static const char *DAY_NAMES[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+CStdString CDateTime::GetAsRFC1123DateTime() const
+{
+  CDateTime time(GetAsUTCDateTime()); 
+  CStdString result;
+  result.Format("%s, %02i %s %04i %02i:%02i:%02i GMT", DAY_NAMES[time.GetDayOfWeek()], time.GetDay(), MONTH_NAMES[time.GetMonth()-1], time.GetYear(), time.GetHour(), time.GetMinute(), time.GetSecond());
+  return result;
+}
\ No newline at end of file
index b1dbc48..5483ace 100644 (file)
@@ -176,11 +176,13 @@ public:
   void GetAsTm(tm& time) const;
   void GetAsTimeStamp(FILETIME& time) const;
 
+  CDateTime GetAsUTCDateTime() const;
   CStdString GetAsDBDateTime() const;
   CStdString GetAsDBDate() const;
   CStdString GetAsLocalizedDate(bool longDate=false) const;
   CStdString GetAsLocalizedTime(const CStdString &format, bool withSeconds=true) const;
   CStdString GetAsLocalizedDateTime(bool longDate=false, bool withSeconds=true) const;
+  CStdString GetAsRFC1123DateTime() const;
 
   void SetValid(bool yesNo);
   bool IsValid() const;
index 81477d8..c768809 100644 (file)
@@ -45,7 +45,7 @@ bool CDirectoryNodeAlbum::GetContent(CFileItemList& items)
   CQueryParams params;
   CollectQueryParams(params);
 
-  bool bSuccess=musicdatabase.GetAlbumsNav(BuildPath(), items, params.GetGenreId(), params.GetArtistId());
+  bool bSuccess=musicdatabase.GetAlbumsNav(BuildPath(), items, params.GetGenreId(), params.GetArtistId(),-1,-1);
 
   musicdatabase.Close();
 
index 5b7a903..991cdc5 100644 (file)
@@ -140,7 +140,7 @@ void CGUIDialogSmartPlaylistRule::OnBrowse()
   else if (m_rule.m_field == CSmartPlaylistRule::FIELD_ALBUM)
   {
     if (m_type.Equals("songs") || m_type.Equals("mixed") || m_type.Equals("albums"))
-      database.GetAlbumsNav("musicdb://6/",items,-1,-1);
+      database.GetAlbumsNav("musicdb://6/",items,-1,-1,-1,-1);
     if (m_type.Equals("musicvideos") || m_type.Equals("mixed"))
     {
       CFileItemList items2;
index d67be63..c7f4294 100644 (file)
@@ -2813,8 +2813,15 @@ bool CMusicDatabase::GetAlbumFromSong(const CSong &song, CAlbum &album)
   return false;
 }
 
-bool CMusicDatabase::GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idArtist)
+bool CMusicDatabase::GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idArtist, int start, int end)
 {
+  //Create limit
+  CStdString limit;
+  if (start >= 0 && end >= 0)
+  {
+    limit.Format(" limit %i,%i", start, end);
+  }
+
   // where clause
   CStdString strWhere;
   if (idGenre!=-1)
@@ -2830,7 +2837,7 @@ bool CMusicDatabase::GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& i
                             "join exgenresong on song.idSong=exgenresong.idSong "
                           "where exgenresong.idGenre=%i"
                           ")"
-                        ") "
+                        ") " + limit
                         , idGenre, idGenre);
   }
 
@@ -2862,15 +2869,15 @@ bool CMusicDatabase::GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& i
                               "select exartistalbum.idAlbum from exartistalbum " // All albums where extra album artists fit
                               "where exartistalbum.idArtist=%i"
                             ")"
-                          ") "
+                          ") " + limit
                           , idArtist, idArtist, idArtist, idArtist);
   }
   else
   { // no artist given, so exclude any single albums (aka empty tagged albums)
     if (strWhere.IsEmpty())
-      strWhere += "where albumview.strAlbum <> ''";
+      strWhere += "where albumview.strAlbum <> ''" + limit;
     else
-      strWhere += "and albumview.strAlbum <> ''";
+      strWhere += "and albumview.strAlbum <> ''" + limit;
   }
 
   bool bResult = GetAlbumsByWhere(strBaseDir, strWhere, "", items);
index 45450ff..e59bba9 100644 (file)
@@ -158,7 +158,7 @@ public:
   bool GetGenresNav(const CStdString& strBaseDir, CFileItemList& items);
   bool GetYearsNav(const CStdString& strBaseDir, CFileItemList& items);
   bool GetArtistsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, bool albumArtistsOnly);
-  bool GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idArtist);
+  bool GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idArtist, int start, int end);
   bool GetAlbumsByYear(const CStdString &strBaseDir, CFileItemList& items, int year);
   bool GetSongsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idArtist,int idAlbum);
   bool GetSongsByYear(const CStdString& baseDir, CFileItemList& items, int year);
index e4614d3..475c1a0 100644 (file)
@@ -237,7 +237,7 @@ void CMusicInfoScanner::FetchAlbumInfo(const CStdString& strDirectory)
   if (strDirectory.IsEmpty())
   {
     m_musicDatabase.Open();
-    m_musicDatabase.GetAlbumsNav("musicdb://3/",items,-1,-1);
+    m_musicDatabase.GetAlbumsNav("musicdb://3/",items,-1,-1,-1,-1);
     m_musicDatabase.Close();
   }
   else
index eb6c0c5..d81bfae 100644 (file)
@@ -30,6 +30,7 @@
 #include "PlayList.h"
 #include "utils/log.h"
 #include "utils/TimeUtils.h"
+#include "MusicInfoTag.h"
 
 using namespace PLAYLIST;
 
@@ -203,6 +204,26 @@ void CPlayListPlayer::Play()
   Play(0);
 }
 
+void CPlayListPlayer::PlaySongId(int songId)
+{
+  if (m_iCurrentPlayList == PLAYLIST_NONE)
+    return;
+
+  CPlayList& playlist = GetPlaylist(m_iCurrentPlayList);
+  if (playlist.size() <= 0) 
+    Play();
+
+  for (int i = 0; i < playlist.size(); i++)
+  {
+    if (playlist[i]->HasMusicInfoTag() && playlist[i]->GetMusicInfoTag()->GetDatabaseId() == songId)
+    {
+      Play(i);
+      return;
+    }
+  }
+  Play();
+}
+
 void CPlayListPlayer::Play(int iSong, bool bAutoPlay /* = false */, bool bPlayPrevious /* = false */)
 {
   if (m_iCurrentPlayList == PLAYLIST_NONE)
index 0b4a5cf..103cd1e 100644 (file)
@@ -58,7 +58,7 @@ public:
    \sa PlayNext
    */
   void PlayPrevious();
-
+  void PlaySongId(int songId);
   void Play();
 
   /*! \brief Start playing a particular entry in the current playlist
index 11dad4a..755e143 100644 (file)
@@ -40,7 +40,13 @@ JSON_STATUS CAVPlaylistOperations::Play(const CStdString &method, ITransportLaye
   if (parameterObject.isInt())
     g_application.getApplicationMessenger().PlayListPlayerPlay(parameterObject.asInt());
   else
-    g_application.getApplicationMessenger().PlayListPlayerPlay();
+  {
+    int songId = (parameterObject.isMember("songid") && parameterObject["songid"].isInt()) ? parameterObject["songid"].asInt() : 0;
+    if (songId > 0)
+      g_application.getApplicationMessenger().PlayListPlayerPlaySongId(songId);
+    else
+      g_application.getApplicationMessenger().PlayListPlayerPlay();
+  }
 
   NotifyAll();
   return ACK;
index 6a6025f..dae8950 100644 (file)
@@ -69,9 +69,11 @@ JSON_STATUS CAudioLibrary::GetAlbums(const CStdString &method, ITransportLayer *
 
   int artistID = ParameterAsInt(param, -1, "artistid");
   int genreID  = ParameterAsInt(param, -1, "genreid");
+  int start = ParameterAsInt(param, -1, "start");
+  int end = ParameterAsInt(param, -1, "end");
 
   CFileItemList items;
-  if (musicdatabase.GetAlbumsNav("", items, genreID, artistID))
+  if (musicdatabase.GetAlbumsNav("", items, genreID, artistID, start, end))
     HandleFileItemList("albumid", false, "albums", items, param, result);
 
   musicdatabase.Close();
index 5768660..a8c8baf 100644 (file)
@@ -302,7 +302,6 @@ JSON_STATUS CJSONRPC::Announce(const CStdString &method, ITransportLayer *transp
 
 CStdString CJSONRPC::MethodCall(const CStdString &inputString, ITransportLayer *transport, IClient *client)
 {
-  CLog::Log(LOGDEBUG, "JSONRPC: %s", inputString.c_str());
   Value inputroot, outputroot, result;
 
   JSON_STATUS errorCode = OK;
index 1ef7f74..ebdb388 100644 (file)
@@ -28,6 +28,7 @@
 #include "../Util.h"
 #include "log.h"
 #include "SingleLock.h"
+#include "DateTime.h"
 #include "addons/AddonManager.h"
 
 #ifdef _WIN32
@@ -111,8 +112,6 @@ int CWebServer::AnswerToConnection(void *cls, struct MHD_Connection *connection,
                       unsigned int *upload_data_size, void **con_cls)
 #endif
 {
-  CLog::Log(LOGNOTICE, "WebServer: %s | %s", method, url);
-
   CWebServer *server = (CWebServer *)cls;
   CStdString strURL = url;
   CStdString originalURL = url;
@@ -210,10 +209,6 @@ int CWebServer::JSONRPC(CWebServer *server, void **con_cls, struct MHD_Connectio
   {
     CStdString *jsoncall = (CStdString *)(*con_cls);
 
-    if (jsoncall->size() > 2000)
-      CLog::Log(LOGINFO, "JSONRPC: Recieved a jsonrpc call wich is longer than 2000 characters, skipping logging it");
-    else
-      CLog::Log(LOGINFO, "JSONRPC: Recieved a jsonrpc call - %s", jsoncall->c_str());
     CHTTPClient client;
     CStdString jsonresponse = CJSONRPC::MethodCall(*jsoncall, server, &client);
 
@@ -261,13 +256,14 @@ int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, co
 {
   int ret = MHD_NO;
   CFile *file = new CFile();
-  if (file->Open(strURL))
+
+  if (file->Open(strURL, READ_NO_CACHE))
   {
     struct MHD_Response *response;
     response = MHD_create_response_from_callback ( file->GetLength(),
                                                    2048,
                                                    &CWebServer::ContentReaderCallback, file,
-                                                   &CWebServer::ContentReaderFreeCallback);
+                                                   &CWebServer::ContentReaderFreeCallback); 
 
     CStdString ext = CUtil::GetExtension(strURL);
     ext = ext.ToLower();
@@ -275,7 +271,12 @@ int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, co
     if (mime)
       MHD_add_response_header(response, "Content-Type", mime);
 
+    CDateTime expiryTime = CDateTime::GetCurrentDateTime();
+    expiryTime += CDateTimeSpan(1, 0, 0, 0);
+    MHD_add_response_header(response, "Expires", expiryTime.GetAsRFC1123DateTime());
+
     ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
+
     MHD_destroy_response(response);
   }
   else
@@ -284,7 +285,6 @@ int CWebServer::CreateFileDownloadResponse(struct MHD_Connection *connection, co
     CLog::Log(LOGERROR, "WebServer: Failed to open %s", strURL.c_str());
     return CreateErrorResponse(connection, MHD_HTTP_NOT_FOUND, GET); /* GET Assumed Temporarily */
   }
-
   return ret;
 }
 
@@ -353,10 +353,12 @@ bool CWebServer::Start(const char *ip, int port)
     // WARNING: when using MHD_USE_THREAD_PER_CONNECTION, set MHD_OPTION_CONNECTION_TIMEOUT to something higher than 1
     // otherwise on libmicrohttpd 0.4.4-1 it spins a busy loop
 
-    // To stream perfectly we should probably have MHD_USE_THREAD_PER_CONNECTION instead of MHD_USE_SELECT_INTERNALLY as it provides multiple clients concurrently
-    m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_IPv6, port, NULL, NULL, &CWebServer::AnswerToConnection, this, MHD_OPTION_END);
+    unsigned int timeout = 60 * 60 * 24;
+    // MHD_USE_THREAD_PER_CONNECTION = one thread per connection
+    // MHD_USE_SELECT_INTERNALLY = use main thread for each connection, can only handle one request at a time [unless you set the thread pool size]
+    m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_IPv6, port, NULL, NULL, &CWebServer::AnswerToConnection, this, MHD_OPTION_THREAD_POOL_SIZE, 8, MHD_OPTION_CONNECTION_LIMIT, 512, MHD_OPTION_CONNECTION_TIMEOUT, timeout, MHD_OPTION_END);
     if (!m_daemon) //try IPv4
-      m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, this, &CWebServer::AnswerToConnection, this, MHD_OPTION_END);
+      m_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, NULL, this, &CWebServer::AnswerToConnection, this, MHD_OPTION_THREAD_POOL_SIZE, 8, MHD_OPTION_CONNECTION_LIMIT, 512, MHD_OPTION_CONNECTION_TIMEOUT, timeout, MHD_OPTION_END);
     m_running = m_daemon != NULL;
     if (m_running)
       CLog::Log(LOGNOTICE, "WebServer: Started the webserver");
@@ -373,7 +375,8 @@ bool CWebServer::Stop()
     MHD_stop_daemon(m_daemon);
     m_running = false;
     CLog::Log(LOGNOTICE, "WebServer: Stopped the webserver");
-  }
+  } else 
+    CLog::Log(LOGNOTICE, "WebServer: Stopped failed because its not running");
 
   return !m_running;
 }