youtubeview.qml Example File | Qt WebKit Examples 5.9

Qt 5.9 | Qt WebKit Examples | YouTube View Example | youtubeview.qml Example File

Qt 5.9.2 Reference Documentation

# youtubeview.qml Example File

webkitqml/youtubeview/youtubeview.qml

      /****************************************************************************
      **
      ** Copyright (C) 2015 The Qt Company Ltd.
      ** Contact: http://www.qt.io/licensing/
      **
      ** 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 The Qt Company Ltd 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 "qrc:/shared" as Shared
      import "qrc:/content" as Content

      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: "qrc:///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"
                  }
              }

              Content.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: "qrc:/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: "qrc:/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()
                  }
              }

              Shared.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
                      }
                  }
              }

              Shared.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()" }
          }
      }