Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
45b5643cc7 | ||
|
eeadc0a8c2 | ||
|
5358deab51 | ||
|
f664548d71 | ||
|
c26d8833b3 | ||
|
32ed78e7bc | ||
|
edc498c510 | ||
|
d02f7302a2 | ||
|
e01edba7f3 | ||
|
0b63621532 | ||
|
89cedd4f7c |
101
form.ui
101
form.ui
@ -6,19 +6,19 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>350</width>
|
<width>650</width>
|
||||||
<height>500</height>
|
<height>500</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>350</width>
|
<width>650</width>
|
||||||
<height>500</height>
|
<height>500</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>350</width>
|
<width>650</width>
|
||||||
<height>500</height>
|
<height>500</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@ -44,7 +44,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>9</x>
|
<x>9</x>
|
||||||
<y>9</y>
|
<y>9</y>
|
||||||
<width>332</width>
|
<width>631</width>
|
||||||
<height>461</height>
|
<height>461</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -52,19 +52,85 @@
|
|||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="groupsListWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>130</width>
|
<width>200</width>
|
||||||
<height>0</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>130</width>
|
<width>200</width>
|
||||||
<height>16777215</height>
|
<height>16777215</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="radioTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Radio</string>
|
||||||
|
</attribute>
|
||||||
|
<widget class="QListWidget" name="radioGroupsListWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>200</width>
|
||||||
|
<height>400</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>200</width>
|
||||||
|
<height>400</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>200</width>
|
||||||
|
<height>400</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="podcastTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Podcasty</string>
|
||||||
|
</attribute>
|
||||||
|
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>-1</x>
|
||||||
|
<y>-1</y>
|
||||||
|
<width>201</width>
|
||||||
|
<height>401</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="podcastGroupsListWidget"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="podcastListWidget">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>195</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>195</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -81,6 +147,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="stopToolButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line">
|
<widget class="Line" name="line">
|
||||||
<property name="lineWidth">
|
<property name="lineWidth">
|
||||||
@ -99,6 +172,18 @@
|
|||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>120</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>70</number>
|
<number>70</number>
|
||||||
</property>
|
</property>
|
||||||
@ -124,7 +209,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>350</width>
|
<width>650</width>
|
||||||
<height>22</height>
|
<height>22</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# This Python file uses the following encoding: utf-8
|
# This Python file uses the following encoding: utf-8
|
||||||
|
|
||||||
API_URL = "https://open.fm/radio/api/v2/ofm/stations_slug.json"
|
|
||||||
DEFAULT_VOLUME = 70
|
DEFAULT_VOLUME = 70
|
||||||
|
|
||||||
from .mainwindow import MainWindow
|
from .mainwindow import MainWindow
|
||||||
|
@ -11,7 +11,7 @@ from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
|
|||||||
from ui_form import Ui_MainWindow
|
from ui_form import Ui_MainWindow
|
||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
from . import API_URL, DEFAULT_VOLUME
|
from . import DEFAULT_VOLUME
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
@ -24,25 +24,36 @@ class MainWindow(QMainWindow):
|
|||||||
self.ui.setupUi(self)
|
self.ui.setupUi(self)
|
||||||
volume_icon = self.style().standardIcon(QStyle.SP_MediaVolume)
|
volume_icon = self.style().standardIcon(QStyle.SP_MediaVolume)
|
||||||
playback_icon = self.style().standardIcon(QStyle.SP_MediaPlay)
|
playback_icon = self.style().standardIcon(QStyle.SP_MediaPlay)
|
||||||
|
stop_icon = self.style().standardIcon(QStyle.SP_MediaStop)
|
||||||
self.ui.volumeToolButton.setIcon(volume_icon)
|
self.ui.volumeToolButton.setIcon(volume_icon)
|
||||||
self.ui.playbackToolButton.setIcon(playback_icon)
|
self.ui.playbackToolButton.setIcon(playback_icon)
|
||||||
|
self.ui.stopToolButton.setIcon(stop_icon)
|
||||||
|
|
||||||
self.__stations_data = self.getStationsData()
|
self.__stations = self.getData(
|
||||||
|
"https://open.fm/radio/api/v2/ofm/stations_slug.json"
|
||||||
|
)
|
||||||
|
self.__podcasts_groups = self.getData(
|
||||||
|
"https://open.fm/api/podcasts/categories"
|
||||||
|
)
|
||||||
self.__player = QMediaPlayer()
|
self.__player = QMediaPlayer()
|
||||||
self.__audio = QAudioOutput()
|
self.__audio = QAudioOutput()
|
||||||
self.__player.setAudioOutput(self.__audio)
|
self.__player.setAudioOutput(self.__audio)
|
||||||
self.setVolume(DEFAULT_VOLUME)
|
self.setVolume(DEFAULT_VOLUME)
|
||||||
self.printGroups()
|
self.printRadioGroups()
|
||||||
|
self.printPodcastGroups()
|
||||||
|
|
||||||
self.ui.groupsListWidget.itemClicked.connect(self.printStations)
|
self.ui.radioGroupsListWidget.itemClicked.connect(self.printRadioStations)
|
||||||
|
self.ui.podcastGroupsListWidget.itemClicked.connect(self.printPodcasts)
|
||||||
|
self.ui.podcastListWidget.itemClicked.connect(self.printPodcastEpisodes)
|
||||||
self.ui.stationsListWidget.itemClicked.connect(self.playRadio)
|
self.ui.stationsListWidget.itemClicked.connect(self.playRadio)
|
||||||
self.ui.playbackToolButton.clicked.connect(self.togglePlayer)
|
self.ui.playbackToolButton.clicked.connect(self.togglePlayer)
|
||||||
|
self.ui.stopToolButton.clicked.connect(self.stopPlayer)
|
||||||
self.ui.volumeHorizontalSlider.valueChanged.connect(self.setVolume)
|
self.ui.volumeHorizontalSlider.valueChanged.connect(self.setVolume)
|
||||||
self.ui.volumeToolButton.clicked.connect(self.toggleMute)
|
self.ui.volumeToolButton.clicked.connect(self.toggleMute)
|
||||||
|
|
||||||
def getStationsData(self) -> dict:
|
def getData(self, url) -> dict:
|
||||||
"""Get JSON data from API and convert it to dict."""
|
"""Get JSON data from API and convert it to dict."""
|
||||||
resp = requests.get(API_URL)
|
resp = requests.get(url)
|
||||||
if resp.status_code not in range(200, 299 + 1):
|
if resp.status_code not in range(200, 299 + 1):
|
||||||
error_box = QMessageBox.critical(
|
error_box = QMessageBox.critical(
|
||||||
self,
|
self,
|
||||||
@ -54,17 +65,25 @@ class MainWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
return json.loads(resp.text)
|
return json.loads(resp.text)
|
||||||
|
|
||||||
def printGroups(self) -> None:
|
def printRadioGroups(self) -> None:
|
||||||
"""Print groups (categories) in groupsListWidget."""
|
"""Print groups (categories) in radioGroupsListWidget."""
|
||||||
self.ui.groupsListWidget.addItems(
|
self.ui.stationsListWidget.clear()
|
||||||
[e["name"] for e in self.__stations_data["groups"]]
|
self.ui.radioGroupsListWidget.addItems(
|
||||||
|
[e["name"] for e in self.__stations["groups"]]
|
||||||
)
|
)
|
||||||
|
|
||||||
def printStations(self) -> None:
|
def printPodcastGroups(self) -> None:
|
||||||
|
"""Print groups (categories) in podcastGroupsListWidget."""
|
||||||
|
self.ui.stationsListWidget.clear()
|
||||||
|
self.ui.podcastGroupsListWidget.addItems(
|
||||||
|
[e["name"] for e in self.__podcasts_groups]
|
||||||
|
)
|
||||||
|
|
||||||
|
def printRadioStations(self) -> None:
|
||||||
"""Print stations (channels) in stationsListWidget."""
|
"""Print stations (channels) in stationsListWidget."""
|
||||||
group = self.ui.groupsListWidget.selectedItems()[0].text()
|
group = self.ui.radioGroupsListWidget.selectedItems()[0].text()
|
||||||
group_id = None
|
group_id = None
|
||||||
for e in self.__stations_data["groups"]:
|
for e in self.__stations["groups"]:
|
||||||
if e["name"] == group:
|
if e["name"] == group:
|
||||||
group_id = e["id"]
|
group_id = e["id"]
|
||||||
|
|
||||||
@ -72,11 +91,44 @@ class MainWindow(QMainWindow):
|
|||||||
self.ui.stationsListWidget.addItems(
|
self.ui.stationsListWidget.addItems(
|
||||||
[
|
[
|
||||||
e["name"]
|
e["name"]
|
||||||
for e in self.__stations_data["channels"]
|
for e in self.__stations["channels"]
|
||||||
if e["group_id"] == group_id
|
if e["group_id"] == group_id
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def printPodcasts(self) -> None:
|
||||||
|
"""Print podcasts in podcastListWidget."""
|
||||||
|
self.ui.stationsListWidget.clear()
|
||||||
|
group = self.ui.podcastGroupsListWidget.selectedItems()[0].text()
|
||||||
|
group_name = None
|
||||||
|
for e in self.__podcasts_groups:
|
||||||
|
if e["name"] == group:
|
||||||
|
group_name = e["slug"]
|
||||||
|
|
||||||
|
self.__podcasts = self.getData(
|
||||||
|
f"https://open.fm/api/podcasts/category/{group_name}"
|
||||||
|
)["podcasts"]["content"]
|
||||||
|
self.ui.podcastListWidget.clear()
|
||||||
|
self.ui.podcastListWidget.addItems(
|
||||||
|
[e["title"] for e in self.__podcasts]
|
||||||
|
)
|
||||||
|
|
||||||
|
def printPodcastEpisodes(self) -> None:
|
||||||
|
"""Print podcast episodes in stationsListWidget."""
|
||||||
|
podcast_name = self.ui.podcastListWidget.selectedItems()[0].text()
|
||||||
|
podcast_id = None
|
||||||
|
for e in self.__podcasts:
|
||||||
|
if e["title"] == podcast_name:
|
||||||
|
podcast_id = e["id"]
|
||||||
|
|
||||||
|
self.__podcast_episodes = self.getData(
|
||||||
|
f"https://open.fm/api/podcast/{podcast_id}/episodes?size=999"
|
||||||
|
)
|
||||||
|
self.ui.stationsListWidget.clear()
|
||||||
|
self.ui.stationsListWidget.addItems(
|
||||||
|
[e["title"] for e in self.__podcast_episodes["content"]]
|
||||||
|
)
|
||||||
|
|
||||||
def setVolume(self, volume: int = None) -> None:
|
def setVolume(self, volume: int = None) -> None:
|
||||||
"""Set playback volume to given number or slider value."""
|
"""Set playback volume to given number or slider value."""
|
||||||
if not volume:
|
if not volume:
|
||||||
@ -98,30 +150,49 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def playRadio(self) -> None:
|
def playRadio(self) -> None:
|
||||||
"""Play station selected by user."""
|
"""Play station selected by user."""
|
||||||
station = self.ui.stationsListWidget.selectedItems()[0].text()
|
selection = self.ui.stationsListWidget.selectedItems()[0].text()
|
||||||
stream_url = None
|
stream_url = None
|
||||||
for e in self.__stations_data["channels"]:
|
for e in self.__stations["channels"]:
|
||||||
if e["name"] == station:
|
if e["name"] == selection:
|
||||||
stream_url = f"http://stream.open.fm/{e['id']}"
|
stream_url = f"http://stream.open.fm/{e['id']}"
|
||||||
|
|
||||||
self.__player.setSource(QUrl(stream_url))
|
if not stream_url:
|
||||||
|
for e in self.__podcast_episodes["content"]:
|
||||||
|
if e["title"] == selection:
|
||||||
|
stream_url = e["file"].replace("https", "http")
|
||||||
|
|
||||||
# Required to avoid crashing. For some reason if you want to change
|
self.__player.setSource(QUrl(stream_url))
|
||||||
# the station for the first time, you need to stop and resume playback.
|
self.__fix_playback()
|
||||||
|
|
||||||
|
def __fix_playback(self) -> None:
|
||||||
|
"""Fix playack."""
|
||||||
|
# For some reason if you want to change the station for the first time,
|
||||||
|
# you need to stop and resume playback.
|
||||||
# If you won't, application would crash.
|
# If you won't, application would crash.
|
||||||
for _ in range(3):
|
self.__player.play()
|
||||||
self.togglePlayer()
|
self.__player.stop()
|
||||||
|
self.__player.play()
|
||||||
|
icon = self.style().standardIcon(QStyle.SP_MediaPause)
|
||||||
|
self.ui.playbackToolButton.setIcon(icon)
|
||||||
|
|
||||||
def togglePlayer(self) -> None:
|
def togglePlayer(self) -> None:
|
||||||
"""Toggle playback (play/stop)."""
|
"""Toggle playback (play/pause)."""
|
||||||
pb_state = QMediaPlayer.PlaybackState
|
pb_state = QMediaPlayer.PlaybackState
|
||||||
if self.__player.playbackState() == pb_state.PlayingState:
|
if self.__player.playbackState() == pb_state.PlayingState:
|
||||||
self.__player.stop()
|
self.__player.pause()
|
||||||
icon = self.style().standardIcon(QStyle.SP_MediaPlay)
|
icon = self.style().standardIcon(QStyle.SP_MediaPlay)
|
||||||
elif self.__player.playbackState() == pb_state.StoppedState:
|
elif self.__player.playbackState() in [
|
||||||
|
pb_state.PausedState,
|
||||||
|
pb_state.StoppedState
|
||||||
|
]:
|
||||||
self.__player.play()
|
self.__player.play()
|
||||||
icon = self.style().standardIcon(QStyle.SP_MediaStop)
|
icon = self.style().standardIcon(QStyle.SP_MediaPause)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.ui.playbackToolButton.setIcon(icon)
|
self.ui.playbackToolButton.setIcon(icon)
|
||||||
|
|
||||||
|
def stopPlayer(self) -> None:
|
||||||
|
self.__player.stop()
|
||||||
|
icon = self.style().standardIcon(QStyle.SP_MediaPlay)
|
||||||
|
self.ui.playbackToolButton.setIcon(icon)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "openfm-qt"
|
name = "openfm-qt"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
description = "Qt client for Polish internet radio Open FM."
|
description = "Qt client for Polish internet radio Open FM."
|
||||||
authors = ["Wiktor Zykubek <wz-git@mailbox.org>"]
|
authors = ["Wiktor Zykubek <wz-git@mailbox.org>"]
|
||||||
license = "ISC"
|
license = "ISC"
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 53 KiB |
Loading…
x
Reference in New Issue
Block a user