diff options
Diffstat (limited to 'examples/webkitqml/youtubeview')
-rw-r--r-- | examples/webkitqml/youtubeview/content/YouTubeDialog.qml | 96 | ||||
-rw-r--r-- | examples/webkitqml/youtubeview/content/player.html | 51 | ||||
-rw-r--r-- | examples/webkitqml/youtubeview/youtubeview.pro | 16 | ||||
-rw-r--r-- | examples/webkitqml/youtubeview/youtubeview.qml | 480 | ||||
-rw-r--r-- | examples/webkitqml/youtubeview/youtubeview.qmlproject | 16 |
5 files changed, 659 insertions, 0 deletions
diff --git a/examples/webkitqml/youtubeview/content/YouTubeDialog.qml b/examples/webkitqml/youtubeview/content/YouTubeDialog.qml new file mode 100644 index 0000000..2d46474 --- /dev/null +++ b/examples/webkitqml/youtubeview/content/YouTubeDialog.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import "../../shared" + +Rectangle { + id: container + + color: "black" + + signal presetClicked(string name) + + property int neededHeight: view.contentItem.childrenRect.height + + ListModel { + id: model + ListElement { + name: "trailers" + } + ListElement { + name: "ClevverMovies" + } + ListElement { + name: "nogoodflix" + } + ListElement { + name: "PalaceFilms" + } + ListElement { + name: "CieonMovies" + } + ListElement { + name: "FilmsActuTrailers" + } + ListElement { + name: "movieclipsTRAILERS" + } + } + + Component { + id: delegate + Button { + buttonWidth: 200 + text: name + onClicked: presetClicked(name) + } + } + + ListView { + id: view + anchors.centerIn: parent + width: 200 + height: (container.neededHeight > parent.height) ? parent.height : container.neededHeight + model: model + delegate: delegate + boundsBehavior: Flickable.StopAtBounds + } +} diff --git a/examples/webkitqml/youtubeview/content/player.html b/examples/webkitqml/youtubeview/content/player.html new file mode 100644 index 0000000..6f3a1e9 --- /dev/null +++ b/examples/webkitqml/youtubeview/content/player.html @@ -0,0 +1,51 @@ +<html> + <head> + <title>-1</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/> + </head> + <body bgcolor="black" marginwidth="0" marginheight="0"> + <!-- The <iframe> (and video player) will replace this <div> tag. --> + <div id="player"></div> + <script> + function getVideoId() { + return window.location.href.slice(window.location.href.indexOf('?') + 1); + } + // This code loads the IFrame Player API code asynchronously. + var tag = document.createElement('script'); + tag.src = "https://www.youtube.com/iframe_api"; + var firstScriptTag = document.getElementsByTagName('script')[0]; + firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); + + // This function creates an <iframe> (and YouTube player) + // after the API code downloads. + var player; + function onYouTubeIframeAPIReady() { + player = new YT.Player('player', { + playerVars: { 'html5': 1, 'iv_load_policy': 3 }, + frameborder: '0', + height: '100%', + width: '100%', + videoId: getVideoId(), + events: { + 'onReady': onPlayerReady, + 'onStateChange': onPlayerStateChange + } + }); + } + + // The API will call this function when the video player is ready. + function onPlayerReady(event) { + document.title = 0; + } + + // The API calls this function when the player's state changes. + function onPlayerStateChange(event) { + if (event.data == YT.PlayerState.PLAYING) { + document.title = 1; + } else if (event.data == YT.PlayerState.ENDED || event.data == YT.PlayerState.PAUSED) { + document.title = 2; + } + } + </script> + </body> +</html> diff --git a/examples/webkitqml/youtubeview/youtubeview.pro b/examples/webkitqml/youtubeview/youtubeview.pro new file mode 100644 index 0000000..30e1ef2 --- /dev/null +++ b/examples/webkitqml/youtubeview/youtubeview.pro @@ -0,0 +1,16 @@ +TEMPLATE = app + +DEFINES += QWEBKIT_EXAMPLE_NAME=\\\"youtubeview\\\" + +QT += quick qml +SOURCES += ../shared/main.cpp + +mac: CONFIG -= app_bundle + +target.path = $$[QT_INSTALL_EXAMPLES]/webkitqml/youtubeview +qml.files = youtubeview.qml content +qml.path = $$[QT_INSTALL_EXAMPLES]/webkitqml/youtubeview +INSTALLS += target qml + +OTHER_FILES += \ + player.html diff --git a/examples/webkitqml/youtubeview/youtubeview.qml b/examples/webkitqml/youtubeview/youtubeview.qml new file mode 100644 index 0000000..4b1dacf --- /dev/null +++ b/examples/webkitqml/youtubeview/youtubeview.qml @@ -0,0 +1,480 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtWebKit 3.0 +import QtQuick.XmlListModel 2.0 +import "content" +import "../shared" + +Rectangle { + id: container + width: 850 + height: 480 + color: "black" + focus: true + + property QtObject videoStatus: QtObject { + property int initial: -1 + property int ready: 0 + property int playing: 1 + property int paused: 2 + } + + QtObject { + id: currentVideo + property string vId: "" + property string title: "" + property int status: videoStatus.initial + } + + readonly property int padding: 20 + + Rectangle { + id: content + anchors.fill: parent + color: "black" + + WebView { + id: webView + anchors.fill: parent + opacity: 0 + + url: "content/player.html?" + currentVideo.vId + + Behavior on opacity { NumberAnimation { duration: 200 } } + + onLoadingChanged: { + switch (loadRequest.status) + { + case WebView.LoadSucceededStatus: + opacity = 1 + return + case WebView.LoadStartedStatus: + case WebView.LoadStoppedStatus: + break + case WebView.LoadFailedStatus: + topInfo.text = "Failed to load the requested video" + break + } + opacity = 0 + } + onTitleChanged: { + currentVideo.status = 1 * title + if (title == videoStatus.paused || title == videoStatus.ready) + panel.state = "list" + else if (title == videoStatus.playing) + panel.state = "hidden" + } + } + + YouTubeDialog { + id: presetDialog + anchors.fill: parent + visible: false + onPresetClicked: { + model.userName = name + model.startIndex = 1 + panel.state = "list" + searchBinding.when = false + presetsBinding.when = true + model.reload() + } + } + } + + Rectangle { + id: panel + height: 100 + color: "black"; + state: "list" + + Behavior on y { NumberAnimation { duration: 200 } } + Behavior on height { NumberAnimation { duration: 200 } } + Behavior on opacity { NumberAnimation { duration: 400 } } + + Binding { id: presetsBinding; target: model; property: "source"; value: model.usersSource; when: false } + Binding { id: searchBinding; target: model; property: "source"; value: model.searchSource; when: false } + + anchors { + left: container.left + right: container.right + } + + states: [ + State { + name: "search" + PropertyChanges { target: panel; color: "black"; opacity: 0.8; y: -height + topInfo.height + searchPanel.height + button.height } + PropertyChanges { target: listView; visible: false } + PropertyChanges { target: searchPanel; opacity: 0.8 } + PropertyChanges { target: hideTimer; running: false } + PropertyChanges { target: presetDialog; visible: true } + }, + + State { + name: "list" + PropertyChanges { target: panel; color: "black"; opacity: 0.8; y: 0 } + PropertyChanges { target: listView; visible: true; focus: true } + PropertyChanges { target: searchPanel; visible: false } + PropertyChanges { target: listView; visible: true } + }, + + State { + name: "hidden" + PropertyChanges { target: panel; color: "gray"; opacity: 0.2; y: -height } + } + ] + + Timer { + id: hideTimer + interval: 3000 + repeat: false + onTriggered: panel.state = "hidden" + } + + ListView { + id: listView + orientation: "Horizontal" + + anchors { + top: panel.top + bottom: button.top + left: panel.left + right: panel.right + } + + focus: true + model: model + + header: Component { + Rectangle { + visible: model.startIndex != 1 && model.status == XmlListModel.Ready + color: "black" + anchors.verticalCenter: parent.verticalCenter + width: height + height: visible ? listView.contentItem.height : 0 + Image { anchors.centerIn: parent; width: 50; height: 50; source: "../shared/images/less.png" } + MouseArea { + anchors.fill: parent + onClicked: model.requestLess() + } + } + } + + footer: Component { + Rectangle { + visible: model.totalResults > model.endIndex && model.status == XmlListModel.Ready + color: "black" + anchors.verticalCenter: parent.verticalCenter + width: height + height: visible ? listView.contentItem.height : 0 + Image { anchors.centerIn: parent; width: 50; height: 50; source: "../shared/images/more.png" } + MouseArea { + anchors.fill: parent + onClicked: model.requestMore() + } + } + } + + delegate: Component { + Image { + source: thumbnail + MouseArea { + anchors.fill: parent + onClicked: { + currentVideo.vId = id + currentVideo.title = title + } + } + Component.onCompleted: { + if (currentVideo.title == "") { + currentVideo.vId = id + currentVideo.title = title + } + } + } + } + + onDraggingChanged: { + if (dragging) + hideTimer.stop() + else if (currentVideo.status == videoStatus.playing) + hideTimer.start() + } + } + + LoadIndicator { + anchors.fill: parent + color: "black" + running: panel.state == "list" && model.status != XmlListModel.Ready + } + + Rectangle { + id: searchPanel + Behavior on opacity { NumberAnimation { duration: 400 } } + + height: searchField.height + container.padding + + anchors { + left: parent.left + right: parent.right + bottom: button.top + } + + opacity: 0 + color: "black" + + gradient: Gradient { + GradientStop { + position: 0.0 + color: "grey" + } + GradientStop { + position: 1.0 + color: "black" + } + } + + Rectangle { + id: searchField + color: "white" + radius: 2 + anchors.centerIn: parent + width: 220 + border.color: "black" + border.width: 2 + height: input.height + container.padding + TextInput { + id: input + color: "black" + anchors.centerIn: parent + horizontalAlignment: TextInput.AlignHCenter + font.capitalization: Font.AllLowercase + maximumLength: 30 + cursorVisible: true + focus: parent.visible + text: "movie trailers" + Keys.onPressed: { + if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) { + model.startIndex = 1 + panel.state = "list" + presetsBinding.when = false + searchBinding.when = true + model.reload() + } + } + } + MouseArea { + anchors.fill: parent + onPressed: input.focus = true + } + } + } + + Button { + id: button + buttonHeight: container.padding + buttonWidth: container.width + fontSize: 8 + + visible: panel.state != "hidden" + + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + + states: [ + State { + name: "search" + PropertyChanges { target: button; text: "Press to switch back to the video list" } + }, + + State { + name: "list" + PropertyChanges { target: button; text: "Press to search for videos" } + } + ] + + state: panel.state + + onClicked: { + if (panel.state == "search") + panel.state = "list" + else + panel.state = "search" + } + } + } + + Rectangle { + height: 10 + color: "black" + opacity: (panel.state == "hidden") ? 0 : 0.8 + + Behavior on opacity { NumberAnimation { duration: 200 } } + + anchors { + top: container.top + left: container.left + right: container.right + } + + Text { + id: topInfo + color: "white" + font.pointSize: 8 + anchors.centerIn: parent + Binding on text { + value: "Results " + model.startIndex + " through " + ((model.endIndex > model.totalResults) ? model.totalResults : model.endIndex) + " out of " + model.totalResults + when: model.status == XmlListModel.Ready && panel.state == "list" && model.count + } + Binding on text { + value: "No results found."; + when: model.state == XmlListModel.Ready && !model.count + } + Binding on text { + value: "Search for videos" + when: panel.state == "search" + } + } + } + + Rectangle { + height: container.padding + color: "black" + opacity: (panel.state == "hidden") ? 0.2 : 0.8 + + Behavior on opacity { NumberAnimation { duration: 200 } } + + anchors { + top: panel.bottom + left: container.left + right: container.right + } + + Text { + id: bottomInfo + color: "white" + font.weight: Font.DemiBold + font.pointSize: 8 + anchors.centerIn: parent + text: { + if (panel.state == "search") + return "Choose from preset video streams" + else + return currentVideo.title + } + } + + MouseArea { + // Responsible for showing and hiding the thumbnail list. + anchors.fill: parent + onPressed: { + if (panel.state != "list") { + panel.state = "list" + if (currentVideo.status == videoStatus.playing) + hideTimer.restart() + } else + panel.state = "hidden" + } + } + } + + XmlListModel { + id: model + + property int totalResults: 0 + property int itemsPerPage: 0 + property int startIndex: 1 + property int endIndex: itemsPerPage + startIndex - 1 + + property string userName: "trailers" + property string baseUrl: "https://gdata.youtube.com/feeds/api" + property string defaultQuery: "alt=rss&orderby=published&v=2&start-index=" + startIndex + property string searchSource: baseUrl + "/videos?" + defaultQuery + "&q=\'" + input.text + "\'" + property string usersSource: baseUrl + "/users/" + userName + "/uploads?" + defaultQuery + + function requestMore() { + startIndex += itemsPerPage + reload() + } + + function requestLess() { + startIndex -= itemsPerPage + reload() + } + + onSourceChanged: { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function(){ + if (xhr.readyState == XMLHttpRequest.DONE) { + if (xhr.status != 200) { + console.log("Something went wrong, received HTTP status code " + xhr.status); + return; + } + var doc = xhr.responseXML.documentElement; + for (var i = 0; i < doc.childNodes.length; ++i) { + var child = doc.childNodes[i]; + for (var j = 0; j < child.childNodes.length; ++j) { + if (child.childNodes[j].nodeName == "itemsPerPage") + itemsPerPage = child.childNodes[j].childNodes[0].nodeValue; + if (child.childNodes[j].nodeName == "totalResults") + totalResults = child.childNodes[j].childNodes[0].nodeValue; + } + } + } + } + xhr.open("GET", source); + xhr.send(); + } + + namespaceDeclarations: "declare namespace media='http://search.yahoo.com/mrss/';declare namespace yt='http://gdata.youtube.com/schemas/2007';" + + source: usersSource + query: "/rss/channel/item" + + XmlRole { name: "id"; query: "media:group/yt:videoid/string()"} + XmlRole { name: "title"; query: "media:group/media:title/string()" } + XmlRole { name: "thumbnail"; query: "media:group/media:thumbnail[1]/@url/string()" } + XmlRole { name: "thumbnailHeight"; query: "media:group/media:thumbnail[1]/@height/number()" } + } +} diff --git a/examples/webkitqml/youtubeview/youtubeview.qmlproject b/examples/webkitqml/youtubeview/youtubeview.qmlproject new file mode 100644 index 0000000..51f0a40 --- /dev/null +++ b/examples/webkitqml/youtubeview/youtubeview.qmlproject @@ -0,0 +1,16 @@ +import QmlProject 1.1 + +Project { + mainFile: "youtubeview.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + JavaScriptFiles { + directory: "." + } + ImageFiles { + directory: "." + } +} |