Initial commit

This commit is contained in:
Niels
2025-06-17 21:25:16 +02:00
commit e7a07a37b5
20 changed files with 900 additions and 0 deletions
+225
View File
@@ -0,0 +1,225 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import Sailfish.WebView 1.0
import Sailfish.WebEngine 1.0
import QtQuick.XmlListModel 2.0
import io.thp.pyotherside 1.5
Page {
id: page
property variant devices: squeezeboxSettings.value("devices",[])
//property variant defaultDevice: squeezeboxSettings.value("defaultDevice","") // squeezeboxSettings.setValue("defaultSource",[ip,port,icon,friendlyName,modelName,source])
property bool running: true
property bool discovering: false
property int retries: 0
function getXml(source) {
var req = new XMLHttpRequest();
req.open("get", source,false);
req.onreadystatechange = function () {
if (req.readyState === 4 && req.status === 200) {
var xml = req.responseText
var friendlyName
var modelName
var icon
var ip
var port
var tmp = source.toString()
tmp = tmp.split("//")
tmp = tmp[1].split(":")
ip = tmp[0]
tmp = tmp[1].split("/")
port = tmp[0]
xml.match(/<friendlyName>(.*?)<\/friendlyName>/g).map(function(val){
friendlyName = val.replace(/<\/?friendlyName>/g,'')
//console.log(friendlyName)
});
xml.match(/<modelName>(.*?)<\/modelName>/g).map(function(val){
modelName = val.replace(/<\/?modelName>/g,'')
//console.log(modelName+" ("+ip+")")
});
xml.match(/<url>(.*?)<\/url>/g).map(function(val){
icon = val.replace(/<\/?url>/g,'')
console.log(val+" ****** 120x120 *****")
});
var patt = /^UpMPD/;
console.log(" ************ PATT: "+patt)
if (patt.test(modelName))
deviceModel.append({"friendlyName": friendlyName,"modelName": modelName,"ip":ip,"port":port,"icon":icon,"source":source});
}
};
req.send();
}
ListModel {id: deviceModel}
Label {
id: volum
text: "VOLUMIO"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Theme.paddingLarge
color: Theme.primaryColor
font.pixelSize: Theme.fontSizeExtraLarge * 2
}
Label {
text: "THE MUSIC PLAYER"
anchors.top: volum.bottom
anchors.horizontalCenter: parent.horizontalCenter
//anchors.topMargin: Theme.paddingLarge
color: Theme.primaryColor
font.pixelSize: Theme.fontSizeSmall
}
Label {
visible: !page.discovering
text: "Choose a device"
anchors.bottom: listview.top
anchors.bottomMargin: Theme.paddingLarge
anchors.horizontalCenter: parent.horizontalCenter
color: Theme.primaryColor
//font.pixelSize: Theme.fontSizeSmall
}
SilicaListView {
id: listview
spacing: Theme.paddingMedium
visible: true
anchors.centerIn: parent
anchors.margins: Theme.paddingSmall
height: contentHeight
width: parent.width
//anchors.fill: parent
//anchors.bottomMargin: button.height
clip: true
model: deviceModel
delegate: BackgroundItem {
Rectangle {
anchors.fill: parent
color: Theme.highlightDimmerColor
}
property variant defVal: []
width: ListView.view.width
height: Theme.itemSizeExtraLarge
Image {
id: img
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: Theme.paddingLarge
height: Theme.itemSizeLarge
width: height
source: "http://"+ip+":"+port+icon
}
Item {
anchors.left: img.right
height: friendlyLabel.height + nameIp.height
anchors.leftMargin: Theme.paddingLarge
anchors.verticalCenter: parent.verticalCenter
Label {
id: friendlyLabel
text: friendlyName
//color: Theme.highlightColor
font.pixelSize: Theme.fontSizeLarge
}
Label {
id: nameIp
text: modelName + " ("+ip+")"
anchors.top: friendlyLabel.bottom
//color: Theme.highlightColor
font.pixelSize: Theme.fontSizeSmall
}
}
onClicked: {
console.log(" ********** http://"+ip+":"+port)
running = false
webPageAddress = "http://"+ip
}
}
}
BusyLabel {
//width: parent.width
text: "Searching for UPnP devices"
running: page.discovering
anchors.centerIn: parent
}
Button {
id: button
text: "Discover"
enabled: !page.discovering
anchors.bottom: parent.bottom
width: parent.width
onClicked: {
deviceModel.clear()
python.startDownload();
}
}
function getIp(url) {
var res = url.split(":");
return res
}
function checkDevices() {
for (var i = 0; i < devices.length; i++) {
}
squeezeboxSettings.setValue("devices",devices)
}
Python {
id: python
Component.onCompleted: {
addImportPath(Qt.resolvedUrl('.'));
setHandler('progress', function(ratio) {
devices.push(ratio)
});
setHandler('finished', function(newvalue) {
page.discovering = false;
button.text = "Discover";
if (retries<3 && devices.length === 0) {
retries = retries + 1
python.startDownload()
} else {
retries = 0
for (var i = 0; i < devices.length; i++) {
getXml(devices[i])
console.log(devices[i]);
}
}
});
importModule('upnpscan', function () {});
}
function startDownload() {
devices = []
page.discovering = true;
button.text = "Discovering";
call('upnpscan.discoverer.discover', function() {});
}
onError: {
console.log('python error: ' + traceback);
}
onReceived: {
console.log('got message from python: ' + data);
}
}
Component.onCompleted: {
python.startDownload();
}
}
+65
View File
@@ -0,0 +1,65 @@
import QtQuick 2.0
import Sailfish.Silica 1.0
import Sailfish.WebView 1.0
WebViewPage {
id: webViewPage
allowedOrientations: Orientation.Portrait | Orientation.Landscape
property string webPageAddress: ""
property string webTitle: ""
property bool webViewLoading: false
property int webViewLoadProgress: 0
Splash {
id: splashItem
visible: running
}
WebView {
id: webView
visible: !splashItem.running
anchors.fill: parent
active: true
url: webPageAddress
onLoadingChanged: {
webViewPage.webViewLoading = loading
webViewPage.webViewLoadProgress = 0
}
onLoadProgressChanged: {
webViewPage.webViewLoadProgress = loadProgress
}
}
Rectangle {
id: panel
color: Theme.highlightDimmerColor
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
//margins: Theme.padding
}
width: parent.width
height: opacity === 0.0 ? 0 : Theme.paddingSmall / 3
radius: 5
opacity: (webViewPage.webViewLoading || loadStatusShowTimer.running) ? 0.75 : 0.0
Behavior on opacity { FadeAnimator {} }
Timer {
id: loadStatusShowTimer
}
Rectangle {
anchors.left: parent.left
color: Theme.secondaryHighlightColor
width: webViewPage.webViewLoading ? parent.width * (webViewPage.webViewLoadProgress / 100) : 0
height: parent.height
}
}
}
+50
View File
@@ -0,0 +1,50 @@
import pyotherside
import threading
import socket
import re
# 'ST:urn:schemas-upnp-org:device:MediaRenderer:1',
def slow_function():
msg = "\r\n".join([
'M-SEARCH * HTTP/1.1',
'HOST:239.255.255.250:1900',
'MAN:"ssdp:discover"',
'ST:urn:schemas-upnp-org:device:MediaRenderer:1',
'MX:2',
'',
'',
])
bytesmsg = msg.encode()
mr = "urn:schemas-upnp-org:device:MediaRenderer:1".encode()
# mr = ".".encode()
se = "(?P<url>https?://[^\s]+)".encode()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.settimeout(3)
s.sendto(bytesmsg, ('239.255.255.250', 1900))
try:
while True:
data = s.recvfrom(65507)
for line in data:
if mr in line:
url = re.search(se, line).group("url")
pyotherside.send('progress', url)
except socket.timeout:
pyotherside.send('finished', "DONE")
class Discoverer:
def __init__(self):
self.bgthread = threading.Thread()
self.bgthread.start()
def discover(self):
if self.bgthread.is_alive():
return
self.bgthread = threading.Thread(target=slow_function)
self.bgthread.start()
discoverer = Discoverer()