diff --git a/.gitignore b/.gitignore index ef93db8..fa563aa 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ android/proguard-project.txt src/gui/qml/deploy.sh .DS_Store android/ant.properties +/.project diff --git a/src/gui/qml/HttpREModel/HttpREModel.qml b/src/gui/qml/HttpREModel/HttpREModel.qml index acb613b..2aff7d1 100644 --- a/src/gui/qml/HttpREModel/HttpREModel.qml +++ b/src/gui/qml/HttpREModel/HttpREModel.qml @@ -10,25 +10,35 @@ ListModel { property int status: XmlListModel.Loading Component.onCompleted: { - var doc = new XMLHttpRequest(); - doc.onreadystatechange = function() { - if (doc.readyState == XMLHttpRequest.DONE) { - var re = new RegExp(regExp, "g"); - //console.log(doc.responseText); - var l = doc.responseText.match(re); - - for (var x=0; x" + model.epNo + "
" + model.time + text: "" + model.title.slim() + "
" + model.time onClicked: { go( Qt.resolvedUrl("../pirateplay.qml"), - { url: "http://www.tv" + currentArgs.n + "play.se" + model.link, - programTitle: "avsnitt " + model.epNo, - programName: model.title.slim(), + { url: "http://www.viafree.se" + model.link, + programName: currentArgs.programName, + programTitle: model.title.slim(), programTime: model.time }, model.index); } diff --git a/src/gui/qml/browser/pirateplay.qml b/src/gui/qml/browser/pirateplay.qml index 29abe2e..f9dbf46 100644 --- a/src/gui/qml/browser/pirateplay.qml +++ b/src/gui/qml/browser/pirateplay.qml @@ -6,18 +6,15 @@ import Components 1.0 Item { anchors.fill: parent +// quality +/* XmlListModel { id: streams source: ppBase() + "/api/get_streams.xml?url=" + currentArgs.url query: "/streams/stream" onStatusChanged: { if (status === XmlListModel.Ready) { - var reqPlayer = get(0).requiredplayerVersion; - //reqPlayer = "1"; - if (reqPlayer != "1") - streamSelect.sourceComponent = streamDialog; - else - streamSelect.sourceComponent = hdsError; + streamSelect.sourceComponent = streamDialog; } } @@ -43,36 +40,19 @@ Item { query: "@required-player-version/string()" } } - +*/ Component { id: streamDialog -// Window { -// width: 720; height: 480 -// visible: true - StreamDialog { /*anchors.verticalCenter: parent.verticalCenter*/ - programMetaInfo: { 'title': currentArgs.programTitle ? currentArgs.programTitle : "", - 'name': currentArgs.programName ? currentArgs.programName : "", - 'season': currentArgs.programSeason ? currentArgs.programSeason : "", - 'time': currentArgs.programTime ? currentArgs.programTime : "", - 'description': currentArgs.programDescription ? currentArgs.programDescription : "" } - } - //} - } - - Component { - id: hdsError - - Item { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - width: childrenRect.width - height: childrenRect.height - - Label { - text: "Pirateplayer kan inte ladda ner denna typ av strömmar.
En guide finns på Pirateplay.se till hur man laddar ner HDS-strömmar manuellt." - onLinkActivated: Qt.openUrlExternally(link) - } - } + StreamDialog { + programMetaInfo: { + 'title': currentArgs.programTitle ? currentArgs.programTitle : "", + 'name': currentArgs.programName ? currentArgs.programName : "", + 'season': currentArgs.programSeason ? currentArgs.programSeason : "", + 'time': currentArgs.programTime ? currentArgs.programTime : "", + 'description': currentArgs.programDescription ? currentArgs.programDescription : "" + } + videoPageUrl: currentArgs.url + } } Component.onDestruction: root.focus = true @@ -81,10 +61,13 @@ Item { id: streamSelect anchors.fill: parent - //focus: true - property variant streamsModel: streams + sourceComponent: streamDialog + +// quality +// property variant streamsModel: streams } - XmlListModelStatusMessage { target: streams } +// quality +// XmlListModelStatusMessage { target: streams } } diff --git a/src/gui/qml/browser/svt/alfabetical.qml b/src/gui/qml/browser/svt/alfabetical.qml index 01712c2..3d0ff86 100644 --- a/src/gui/qml/browser/svt/alfabetical.qml +++ b/src/gui/qml/browser/svt/alfabetical.qml @@ -8,18 +8,18 @@ AzListView { model: HttpREModel { id: indexModel - source: "http://svtplay.se/program" - regExp: "((.|\n)*?)" + source: "http://www.svtplay.se/program" + regExp: "
  • ]*>(]*>[^<]*)
  • " roles: [ ReRole { name: "text" - regExp: ">([^<]+)<" + regExp: ">([^<]+)" function decode(s) { return s.slim().decodeHTMLEntities(); } }, ReRole { name: "link" - regExp: "href=\"([^\"]+)\"" + regExp: 'href="([^"]+)"' } ] } @@ -28,7 +28,7 @@ AzListView { text: model.text onClicked: { go( Qt.resolvedUrl("program.qml"), - { url: "http://www.svtplay.se" + model.link + "?tab=episodes&sida=999", + { url: "http://www.svtplay.se" + model.link, programName: model.text }, model.index); } diff --git a/src/gui/qml/browser/svt/open_a-z.qml b/src/gui/qml/browser/svt/open_a-z.qml index 41b4aaa..c7332ad 100644 --- a/src/gui/qml/browser/svt/open_a-z.qml +++ b/src/gui/qml/browser/svt/open_a-z.qml @@ -1,30 +1,40 @@ import QtQuick 1.1 import Components 1.0 +import "../../HttpREModel" import "../../common.js" as Common AzListView { - model: XmlListModel { - source: "tidy://www.oppetarkiv.se/kategori/titel" - query: "//li[@class=\"svtoa-anchor-list-item\"]/a" + model: HttpREModel { + id: indexModel - XmlRole { - name: "text" - query: "string()" - } - XmlRole { - name: "link" - query: "@href/string()" - } + source: "https://www.oppetarkiv.se/program" + regExp: "
  • ]*>(]*>[^<]*)
  • " + + roles: [ + ReRole { + name: "text" + regExp: ">([^<]+)" + function decode(s) { return s.slim().decodeHTMLEntities(); } + }, + ReRole { + name: "link" + regExp: 'href="([^"]+)"' + } + ] } + delegate: ListDelegate { text: model.text.slim() onClicked: { go( Qt.resolvedUrl("open_program.qml"), - { url: decodeURIComponent(model.link.replace("http://", "tidy://")), - programName: model.text.slim() }, - model.index ); + { + url: "https://www.oppetarkiv.se" + decodeURI(model.link), + programName: model.text.slim() + }, + model.index ); } } } + diff --git a/src/gui/qml/browser/svt/open_program.qml b/src/gui/qml/browser/svt/open_program.qml index 462be08..a06e91d 100644 --- a/src/gui/qml/browser/svt/open_program.qml +++ b/src/gui/qml/browser/svt/open_program.qml @@ -5,42 +5,42 @@ import "../../common.js" as Common BarredListView { model: XmlListModel { - id: indexModel - - Component.onCompleted: source = currentArgs.url - query: "//div[@class=\"svt-display-table-xs\"]" - + id: programModel + source: currentArgs.url + query: '//div[@class="svt-display-table-xs"]' + XmlRole { name: "title" - query: "div/div/h2/a/string()" + query: "div/div/*/a/string()" } XmlRole { name: "link" - query: "div/div/h2/a/@href/string()" + query: "div/div/*/a/@href/string()" } XmlRole { name: "date" - query: "div/div//time/string()" + query: "div/div/p/time/string()" } XmlRole { name: "thumb" - query: "div/figure/img/@data-imagename/string()" + query: "div/figure/img/@src/string()" } } - delegate: ListDelegate { imgSource: model.thumb text: "" + model.title.slim() + " - " + model.date.slim() onClicked: { go( Qt.resolvedUrl("../pirateplay.qml"), - { url: model.link, - programName: currentArgs.programName, - programTitle: model.title.slim(), - programTime: model.date }, - model.index); + { + url: "http://www.oppetarkiv.se" + model.link, + programName: currentArgs.programName, + programTitle: model.title.slim(), + programTime: model.date + }, + model.index); } } - XmlListModelStatusMessage { target: indexModel } + XmlListModelStatusMessage { target: programModel } } diff --git a/src/gui/qml/browser/svt/program.qml b/src/gui/qml/browser/svt/program.qml index 42dab14..307efda 100644 --- a/src/gui/qml/browser/svt/program.qml +++ b/src/gui/qml/browser/svt/program.qml @@ -1,45 +1,38 @@ import QtQuick 1.1 import Components 1.0 -import "../../HttpREModel" +import "../../JSONListModel" import "../../common.js" as Common BarredListView { anchors.fill: parent - model: HttpREModel { + model: JSONListModel { id: programModel source: currentArgs.url ? currentArgs.url : "" - regExp: "(
    ]+>(.|\n)*?
    )" - - roles: [ - ReRole { - name: "text" - regExp: 'data-title="([^"]+)"' - function decode(s) { return s.slim().decodeHTMLEntities(); } - }, - ReRole { - name: "link" - regExp: "href=\"([^\"]+)\"" - }, - ReRole { - name: "thumb" - regExp: "src=\"([^\"]+)\"" - } - ] + regExp: "__svtplay'] = ({.*});" + query: [ + '$.videoTitlePage.relatedVideosTabs[?(@.type=="RELATED_VIDEO_TABS_LATEST")].videos[*]', + '$.videoTitlePage.relatedVideosTabs[?(@.type=="RELATED_VIDEO_TABS_CLIP")].videos[*]' + ] } - - delegate: ListDelegate { - imgSource: model.thumb.startsWith("http://") ? model.thumb : "http://svtplay.se" + model.thumb - text: model.text + text: + "" + (model.season > 0 ? "Säsong " + model.season + " - " : "") + + model.title + "" + + (model.broadcastDate ? " - " + model.broadcastDate.replace(/:..\+.*/, "").replace("T", " ") : "") onClicked: { - var base = (model.link.startsWith("http") ? "" : "http://svtplay.se"); + var pageUrl = "http://www.svtplay.se" + encodeURI(model.contentUrl); go( Qt.resolvedUrl("../pirateplay.qml"), - { url: base + model.link, programTitle: model.text, programName: currentArgs.programName }, - model.index); + { + url: pageUrl, + programTitle: model.title, + programName: currentArgs.programName, + programSeason: (model.season > 0 ? "Säsong " + model.season : "") + }, + model.index); } } diff --git a/src/gui/qml/browser/tv4/a-z.qml b/src/gui/qml/browser/tv4/a-z.qml index 8ce3709..7e1a432 100644 --- a/src/gui/qml/browser/tv4/a-z.qml +++ b/src/gui/qml/browser/tv4/a-z.qml @@ -1,30 +1,29 @@ import QtQuick 1.1 import Components 1.0 +import "../../JSONListModel" import "../../common.js" as Common AzListView { - model: XmlListModel { - source: "tidy://www.tv4play.se/program?per_page=999&per_row=4&page=1&content-type=a-o&is_premium=false" - query: "//ul[@class=\"row a-o-list-wrapper\"]/li/ul/li/a" - XmlRole { - name: "text" - query: "string()" - } - XmlRole { - name: "link" - query: "@href/string()" - } + model: JSONListModel { + id: indexModel + + source: "http://webapi.tv4play.se/play/programs?is_premium=false" + query: "$[*]" } - delegate: ListDelegate { - text: model.text.slim() + delegate: ListDelegate { + text: model.name onClicked: { - go( Qt.resolvedUrl("program.qml"), - { url: decodeURIComponent(model.link).replace("http://", "tidy://"), - programName: model.text.slim() }, - model.index ); + go( Qt.resolvedUrl("program.qml"), + { + url: "http://webapi.tv4play.se/play/video_assets?is_live=false&platform=web&per_page=99999&node_nids=" + model.nid, + programName: model.name + }, + model.index + ); } } + } diff --git a/src/gui/qml/browser/tv4/program.qml b/src/gui/qml/browser/tv4/program.qml index 0ab94e4..3f1c339 100644 --- a/src/gui/qml/browser/tv4/program.qml +++ b/src/gui/qml/browser/tv4/program.qml @@ -1,82 +1,39 @@ import QtQuick 1.1 import Components 1.0 +import "../../JSONListModel" import "../../common.js" as Common -Item { - id: list +BarredListView { + anchors.fill: parent - XmlListModel { + model: JSONListModel { id: programModel - Component.onCompleted: source = currentArgs.url - query: "//a[@class=\"js-show-more btn secondary full\" and contains(@data-more-from, 'type=video')]" - - onStatusChanged: { - if (status === XmlListModel.Ready) { - try { - episodesList.model.source = "tidy://www.tv4play.se" + decodeURIComponent(get(0).link) + "&page=0&per_page=400"; - } catch (err) { - if (err.name === 'TypeError') - episodesList.model.source = decodeURIComponent(programModel.source); - else - throw err; - } - } - } - - XmlRole { - name: "link" - query: "@data-more-from/string()" - } + source: currentArgs.url ? currentArgs.url : "" + query: [ + "$.results[?(@.full_program)]", + "$.results[?(!@.full_program)]" + ] } - BarredListView { - id: episodesList - - anchors.fill: parent - - model: XmlListModel { - query: "//div[@class=\"js-search-page\"]//li[not(descendant::p[starts-with(@class, 'requires-authorization')])]" - - XmlRole { - name: "text" - query: "div/h3/string()" - } - XmlRole { - name: "link" - query: "div/h3/a/@href/string()" - } - XmlRole { - name: "date" - query: "div/div[@class=\"video-meta\"]/p[@class=\"date\"]/string()" - } - XmlRole { - name: "image" - query: "p[@class=\"video-picture\"]/a/img/@src/string()" - } - } - delegate: ListDelegate { - imgSource: { - var url = model.image; - var pairs = url.split("&"); - for (var i=0; i" + model.date - onClicked: { - go( Qt.resolvedUrl("../pirateplay.qml"), - { url: "http://tv4play.se" + model.link, - programTitle: model.text.slim(), - programName: currentArgs.programName, - programTime: model.date}, - model.index ); - } + delegate: ListDelegate { + text: + "" + (model.season > 0 ? "Säsong " + model.season + " - " : "") + + model.title + " - " + + model.broadcast_date_time.replace(/:..\+.*/, "").replace("T", " ") + onClicked: { + var pageUrl = "http://www.tv4play.se/program/" + encodeURI(model.program_nid) + "?video_id=" + model.id; + go( Qt.resolvedUrl("../pirateplay.qml"), + { + url: pageUrl, + programTitle: model.title, + programName: currentArgs.programName, + programSeason: (model.season > 0 ? "Säsong " + model.season : "") + }, + model.index); } } - XmlListModelStatusMessage { target: episodesList.model } + XmlListModelStatusMessage { target: programModel } } diff --git a/src/gui/qml/imports/_common/Components/StreamDialog.qml b/src/gui/qml/imports/_common/Components/StreamDialog.qml index 179bb03..92acc1e 100644 --- a/src/gui/qml/imports/_common/Components/StreamDialog.qml +++ b/src/gui/qml/imports/_common/Components/StreamDialog.qml @@ -4,6 +4,7 @@ import QtDesktop 0.1 Flickable { property variant programMetaInfo: { "title": "", "name": "", "season": "", "time": "", "description": "" } + property string videoPageUrl: "" anchors.fill: parent contentHeight: column.height @@ -13,7 +14,8 @@ Flickable { } Column { - property variant selectedStream: qualityBox.model.get(qualityBox.selectedIndex) +// quality +// property variant selectedStream: qualityBox.model.get(qualityBox.selectedIndex) id: column width: parent.width; height: childrenRect.height @@ -27,7 +29,7 @@ Flickable { id: headerLabel anchors.verticalCenter: parent.verticalCenter; anchors.horizontalCenter: parent.horizontalCenter font.bold: true - text: "Spara eller spela upp ström" + text: "Spara media" } } @@ -41,12 +43,7 @@ Flickable { FilePathField { id: fileName filter: { - if (column.selectedStream.suffixHint == "mp4") - return "MPEG4 (*.mp4);;TeleStream (*.ts)"; - else if (column.selectedStream.suffixHint == "flv") - return "Flashvideo (*.flv)"; - else - return "Alla filer (*.*)"; + return "MP4 (*.mp4)"; } width: parent.width path: { @@ -65,7 +62,7 @@ Flickable { newFileName = newFileName.replace(/%season%/g, season); newFileName = newFileName.replace(/%time%/g, time); newFileName = newFileName.replace(/%description%/g, description); - newFileName = userSettings.startDir + "/" + newFileName + "." + streamsModel.get(0).suffixHint; + newFileName = userSettings.startDir + "/" + newFileName + ".mp4"; newFileName = pathToNativeSeparators(newFileName); return newFileName; } @@ -105,66 +102,62 @@ Flickable { FormRow { label: "" - visible: column.selectedStream.subtitles + visible: true CheckBox { id: saveSubs - text: "Spara undertexter (till: " + (pathIsDir(fileName.text) ? "" : baseName(fileName.path)) + "." + suffix(column.selectedStream.subtitles).replace("?", "") + ")" + text: "Spara undertexter" width: parent.width } } - FormRow { - label: "Kvalitet" - - ComboBox { - id: qualityBox - model: streamsModel - } - } - - ToggleBox { - label: "timeralternativ" - width: parent.width - - FormLayout { - labelColumnWidth: formLayout.labelColumnWidth; customLabelColumnWidth: true - FormRow { - label: "Fördröjning" - - TimerField { id: delay } - } - FormRow { - label: "Längd" - TimerField { id: duration } - } - FormRow { - label: "" - Label { text: "Ange noll för obestämd längd" } - } - } - } +// quality +// FormRow { +// label: "Kvalitet" +// +// ComboBox { +// id: qualityBox +// model: streamsModel +// } +// } FormRow { label: "" - Button { - id: playBtn - text: "Spela upp" - - onClicked: play(column.selectedStream.url, column.selectedStream.subtitles, userSettings.playerCmd) - } Button { id: downloadBtn text: "Starta nedladdning" enabled: !isDir.when && !isEmpty.when && !isWritable.when onClicked: { - downloadStack.addDownload(column.selectedStream.url, fileName.path, delay.ms, duration.ms); - if (saveSubs.checked) { - var subFilePath = absDir(fileName.path) + nativeSeparator() + baseName(fileName.path) + "." + suffix(column.selectedStream.subtitles).replace("?", ""); - downloadStack.addDownload(column.selectedStream.subtitles, subFilePath, delay.ms, duration.ms); - } + var out = fileName.path + .replace(/\.mp4$/i, "") + .replace(/[åäöÅÄÖ\.]/g, function(match) { + return { + "å": "a", + "ä": "a", + "ö": "o", + "Å": "A", + "Ä": "A", + "Ö": "O", + ".": "_" + }[match]; + }); + var xhr = new XMLHttpRequest; + var url = + "http://localhost:1199/download" + + "?url=" + encodeURIComponent(videoPageUrl) + + "&out=" + encodeURIComponent(out) + + "&sub=" + (saveSubs.checked ? "true" : "false"); + xhr.open("GET", url); + xhr.send(); + +// old/quality +// downloadStack.addDownload(column.selectedStream.url, fileName.path, delay.ms, duration.ms); +// if (saveSubs.checked) { +// var subFilePath = absDir(fileName.path) + nativeSeparator() + baseName(fileName.path) + "." + suffix(column.selectedStream.subtitles).replace("?", ""); +// downloadStack.addDownload(column.selectedStream.subtitles, subFilePath, delay.ms, duration.ms); +// } } } } diff --git a/src/gui/qml/imports/_common/Components/UserSettings.qml b/src/gui/qml/imports/_common/Components/UserSettings.qml index 47c8d75..c1c3872 100644 --- a/src/gui/qml/imports/_common/Components/UserSettings.qml +++ b/src/gui/qml/imports/_common/Components/UserSettings.qml @@ -1,9 +1,9 @@ import QtQuick 1.1 Item { - property variant defaults: { "startDir": getHomeDir(), + property variant defaults: { "startDir": getHomeDir().match(/^(.*?)[\/\\]?$/), "playerCmd": (mac() ? applicationDirPath() + "/" : "") + "ffplay \"%0\"", - "filenameTemplate": "%name%_-_%title%", + "filenameTemplate": "%name%_%season%_%title%", "fontPointSize": getSetting("MainWindow/font_point_size") } property string startDir: getSetting("Location/start_dir", defaults.startDir) diff --git a/src/gui/qml/main.qml b/src/gui/qml/main.qml index 6e6ea5b..e9efd83 100644 --- a/src/gui/qml/main.qml +++ b/src/gui/qml/main.qml @@ -63,15 +63,6 @@ MainWindow { source: "browser/main.qml" } } - - TabItem { - title: "Nedladdningar (" + downloadStack.count + ")" - - Loader { - anchors.fill: parent - source: "downloads.qml" - } - } } NewsBox {