Skip to content

Commit

Permalink
Podcasts fix and packaging reworks
Browse files Browse the repository at this point in the history
- Podcasts download can be cancelled now
- Partially downloaded files are removed after cancelling
- Add app icon and use that for binary distribution
- Add desktop file for packaging
- Prepare for .whl distribution
- Add metadata for podcasts
- Allow alternate file formats for podcasts
-
  • Loading branch information
casualsnek committed Apr 28, 2023
1 parent b262672 commit 7626e08
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 19 deletions.
7 changes: 6 additions & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
from gui.minidialog import MiniDialog
from runtimedata import get_logger

if __name__ == '__main__':

def main():
logger = get_logger('__init__')
logger.info('Starting application in \n3\n2\n1')
app = QApplication(sys.argv)
_dialog = MiniDialog()
window = MainWindow(_dialog)
app.setDesktopFileName('org.eu.casualsnek.onthespot')
app.exec_()
logger.info('Good bye ..')

if __name__ == '__main__':
main()
6 changes: 5 additions & 1 deletion build_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,20 @@ if [ -f "ffbin_nix/ffmpeg" ]; then
echo " => Found 'ffbin_win' directory and ffmpeg binary.. Using ffmpeg binary append mode "
pyinstaller --onefile \
--add-data="gui/qtui/*.ui:qui/qtui" \
--add-data="resources/*.png:resources" \
--add-binary="ffbin_nix/*:bin/ffmpeg" \
--paths="." \
--name="onthespot_linux" \
--name="onthespot_linux_ffm" \
--icon="resources/icon.png" \
__init__.py
else
echo " => Building to use ffmpeg binary from system... "
pyinstaller --onefile \
--add-data="gui/qtui/*.ui:gui/qtui" \
--add-data="resources/*.png:resources" \
--paths="." \
--name="onthespot_linux" \
--icon="resources/icon.png" \
__init__.py
fi
echo " => Setting permissions.. "
Expand Down
6 changes: 3 additions & 3 deletions build_winC2.bat
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ pip install simpleaudio
pip install -r requirements.txt
if exist ffbin_win\ffmpeg.exe (
echo =^> Found 'ffbin_win' directory and ffmpeg binary.. Using ffmpeg binary append mode
pyinstaller --onefile --noconfirm --add-binary="ffbin_win/*.exe;bin/ffmpeg" --add-data="gui/qtui/*.ui;gui/qtui" --paths="." --name="onthespot_win" __init__.py
pyinstaller --onefile --noconfirm --add-data="gui/qtui/*.ui;gui/qtui" --add-data="resources/*.png;resources" --add-binary="ffbin_win/*.exe;bin/ffmpeg" --paths="." --name="onthespot_win_ffm" --icon="resources/icon.png" __init__.py
) else (
echo =^> Building to use ffmpeg binary from system...
pyinstaller --onefile --noconfirm --add-data="gui/qtui/*.ui;gui/qtui" --paths="." --name="onthespot_win" __init__.py
pyinstaller --onefile --noconfirm --add-data="gui/qtui/*.ui;gui/qtui" --add-data="resources/*.png;resources" --paths="." --name="onthespot_win" --icon="resources/icon.png" __init__.py
)
echo =^> Cleaning..
del /F /Q /A onthespot_win.spec
rmdir build /s /q
rmdir __pycache__ /s /q
echo =^> Done
echo =^> Done
4 changes: 3 additions & 1 deletion gui/mainui.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import queue
import time
import uuid
from PyQt5 import uic, QtNetwork
from PyQt5 import uic, QtNetwork, QtGui
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QMainWindow, QHeaderView, QLabel, QPushButton, QProgressBar, QTableWidgetItem, QFileDialog
from exceptions import EmptySearchResultException
Expand Down Expand Up @@ -78,7 +78,9 @@ class MainWindow(QMainWindow):
def __init__(self, _dialog):
super(MainWindow, self).__init__()
self.path = os.path.dirname(os.path.realpath(__file__))
icon_path = os.path.join(config.app_root, 'resources', 'icon.png')
uic.loadUi(os.path.join(self.path, "qtui", "main.ui"), self)
self.setWindowIcon(QtGui.QIcon(icon_path))
logger.info("Initialising main window")
self.group_search_items.hide()
# Bind button click
Expand Down
4 changes: 4 additions & 0 deletions gui/qtui/main.ui
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
<property name="windowTitle">
<string>casualOnTheSpot</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>../../resources/icon.png</normaloff>../../resources/icon.png</iconset>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
Expand Down
9 changes: 9 additions & 0 deletions org.eu.casualsnek.onthespot.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

[Desktop Entry]
Type=Application
Name=OnTheSpot
GenericName=Music downloader
Comment=Simple and easy to use audio media downloader
Icon=casual_onthespot
Exec=onthespot_gui
Categories=Utilities
7 changes: 4 additions & 3 deletions otsconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def __init__(self, cfg_path=None):
"max_retries": 3,
"max_search_results": 10,
"media_format": "mp3",
"podcast_media_format": "mp3",
"force_raw": False,
"force_premium": False,
"chunk_size": 50000,
Expand All @@ -57,12 +58,12 @@ def __init__(self, cfg_path=None):
os.makedirs(os.path.dirname(self.get("log_file")), exist_ok=True)

# Set ffmpeg path
app_root = os.path.dirname(os.path.realpath(__file__))
if os.path.isfile(os.path.join(app_root, 'bin', 'ffmpeg', 'ffmpeg' + self.ext_)):
self.app_root = os.path.dirname(os.path.realpath(__file__))
if os.path.isfile(os.path.join(self.app_root, 'bin', 'ffmpeg', 'ffmpeg' + self.ext_)):
# Try embedded binary at first
print('FFMPEG found in package !')
self.set_('_ffmpeg_bin_path',
os.path.abspath(os.path.join(app_root, 'bin', 'ffmpeg', 'ffmpeg' + self.ext_)))
os.path.abspath(os.path.join(self.app_root, 'bin', 'ffmpeg', 'ffmpeg' + self.ext_)))
elif os.path.isfile(os.path.join(self.get('ffmpeg_bin_dir', '.'), 'ffmpeg' + self.ext_)):
# Now try user defined binary path
print('FFMPEG found at config:ffmpeg_bin_dir !')
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
Binary file added resources/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
90 changes: 90 additions & 0 deletions resources/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[metadata]
name = onthespot
version = 0.5
description = A simple and easy to use media downloader.
long_description = file: README.md
long_description_content_type = text/markdown
author = Casual Snek
author_email = casualsnek@pm.me
url = https://github.com/causalsnek/onthespot
keywords = music, downloader, audio, onthespot
license = GPL-2.0 License

[options]
package_dir=
=.
python_requires = >= 3.0
packages = find:
include_package_data = True

[options.entry_points]
console_scripts =
onthespot_gui = onthespot:main

[options.packages.find]
where=.

[options.package_data]
* = *.ui, *.png
10 changes: 6 additions & 4 deletions utils/spotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ def conv_artist_format(artists):
def set_audio_tags(filename, metadata, track_id_str):
logger.info(
f"Setting tags for audio media at '{filename}', mediainfo -> '{metadata}'")
type_ = 'track'
tags = music_tag.load_file(filename)
for key in metadata.keys():
value = metadata[key]
Expand All @@ -182,14 +183,16 @@ def set_audio_tags(filename, metadata, track_id_str):
elif key == 'lyrics':
tags['lyrics'] = value
elif key == 'genre':
if 'Podcast' in value or 'podcast' in value:
type_ = 'episode'
tags['genre'] = conv_artist_format(value)
elif key in ['total_tracks', 'totaltracks']:
tags['totaltracks'] = value
elif key in ['total_discs', 'totaldiscs', 'total_disks', 'totaldisks']:
tags['totaldiscs'] = value
elif key == 'isrc':
tags['isrc'] = value
tags['comment'] = 'id[spotify.com:track:' + track_id_str + ']'
tags['comment'] = f'id[spotify.com:{type_}:{track_id_str}]'
tags.save()


Expand Down Expand Up @@ -288,11 +291,10 @@ def get_episode_info(session, episode_id_str):
token = session.tokens().get("user-read-email")
info = json.loads(requests.get("https://api.spotify.com/v1/episodes/" +
episode_id_str, headers={"Authorization": "Bearer %s" % token}).text)

if "error" in info:
return None, None
return None, None, None
else:
return sanitize_data(info["show"]["name"]), sanitize_data(info["name"])
return sanitize_data(info["show"]["name"]), sanitize_data(info["name"]), get_thumbnail(info['images']), info['release_date'], info['show']['total_episodes'], info['show']['publisher']


def get_show_episodes(session, show_id_str):
Expand Down
41 changes: 37 additions & 4 deletions worker/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import subprocess
import time
import traceback

import requests
from PyQt5.QtCore import QObject, pyqtSignal
from librespot.audio.decoders import AudioQuality, VorbisOnlyAudioQuality
from librespot.metadata import TrackId, EpisodeId
Expand Down Expand Up @@ -127,6 +129,9 @@ def download_track(self, session, track_id_str, extra_paths="", force_album_form
self.progress.emit([trk_track_id_str, "Cancelled", [0, 100]])
cancel_list.pop(trk_track_id_str)
self.__last_cancelled = True
if os.path.exists(filename):
file.close()
os.remove(filename)
return False
data = stream.input_stream.stream().read(_CHUNK_SIZE)
self.logger.debug(
Expand Down Expand Up @@ -217,7 +222,7 @@ def download_episode(self, session, episode_id_str, extra_paths=""):
self.logger.info(f"Downloading episode by id '{episode_id_str}'")
podcast_path = os.path.join(config.get("download_root"), config.get("podcast_subdir", "Podcasts"))
quality = AudioQuality.HIGH
podcast_name, episode_name = get_episode_info(session, episode_id_str)
podcast_name, episode_name, thumbnail, release_date, total_episodes, artist = get_episode_info(session, episode_id_str)
skip_existing_file = True
if extra_paths == "":
extra_paths = os.path.join(extra_paths, podcast_name)
Expand All @@ -228,25 +233,33 @@ def download_episode(self, session, episode_id_str, extra_paths=""):
else:
try:
filename = podcast_name + " - " + episode_name

episode_id = EpisodeId.from_base62(episode_id_str)
stream = session.content_feeder().load(episode_id, VorbisOnlyAudioQuality(quality), False, None)
os.makedirs(os.path.join(podcast_path, extra_paths), exist_ok=True)
total_size = stream.input_stream.size
downloaded = 0
_CHUNK_SIZE = config.get("chunk_size")
fail = 0
file_path = os.path.join(podcast_path, extra_paths, filename + ".wav")
extension = config.get('podcast_media_format', 'mp3')
file_path = os.path.join(podcast_path, extra_paths, filename + f".{extension}")
if os.path.isfile(file_path) and os.path.getsize(file_path) and skip_existing_file:
self.logger.info(f"Episode by id '{episode_id_str}', already exists.. Skipping ")
self.progress.emit([episode_id_str, "Downloaded", [100, 100], file_path, filename])
return True
with open(file_path, 'wb') as file:
while downloaded <= total_size:
if episode_id_str in cancel_list:
self.progress.emit([episode_id_str, "Cancelled", [0, 100]])
cancel_list.pop(episode_id_str)
self.__last_cancelled = True
if os.path.exists(file_path):
file.close()
os.remove(file_path)
return False
data = stream.input_stream.stream().read(_CHUNK_SIZE)
downloaded += len(data)
file.write(data)
self.progress.emit([episode_id_str, None, [downloaded, total_size]])
self.progress.emit([episode_id_str, None, [downloaded, total_size], file_path, filename])
if (total_size - downloaded) < _CHUNK_SIZE:
_CHUNK_SIZE = total_size - downloaded
if len(data) == 0:
Expand All @@ -256,6 +269,26 @@ def download_episode(self, session, episode_id_str, extra_paths=""):
break
if downloaded >= total_size:
self.logger.info(f"Episode by id '{episode_id_str}', downloaded")
if extension not in ['ogg', 'wav']:
self.progress.emit([episode_id_str, "Converting", None, file_path, filename])
convert_audio_format(file_path, quality)
self.logger.info(f'Writing metadata for episode "{episode_id_str}" ')
self.progress.emit([episode_id_str, "Writing metadata", None, file_path, filename])
set_audio_tags(
file_path,
{
'name': episode_name,
'album_name': podcast_name,
'release_year': release_date,
'total_tracks': total_episodes,
'artists': [artist],
'genre': ['Podcast']
},
episode_id_str
)
self.progress.emit([episode_id_str, "Setting thumbnail", None, file_path, filename])
self.logger.info(f'Setting thumbnail for episode "{episode_id_str}" ')
set_music_thumbnail(file_path, thumbnail)
self.progress.emit([episode_id_str, "Downloaded", [100, 100], file_path, filename])
return True
else:
Expand Down
4 changes: 2 additions & 2 deletions worker/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def run(self):
if not item['data'].get('hide_dialogs', False):
self.progress.emit('Episodes are being parsed and will be added to download queue shortly !')
for episode_id in get_show_episodes(session, item['media_id']):
show_name, episode_name = get_episode_info(session, episode_id)
show_name, episode_name, thumbnail, release_date, total_episodes, artist = get_episode_info(session, episode_id)
logger.info(
f"PQP parsing podcast : {show_name}:{item['media_id']}, "
f"episode item: {episode_name}:{episode_id}"
Expand All @@ -166,7 +166,7 @@ def run(self):
if not item['data'].get('hide_dialogs', False):
self.progress.emit(f"Added show '{show_name}' to download queue!")
elif item['media_type'] == 'episode':
podcast_name, episode_name = get_episode_info(session, item['media_id'])
podcast_name, episode_name, thumbnail, release_date, total_episodes, artist = get_episode_info(session, item['media_id'])
logger.info(f"PQP parsing podcast episode : {episode_name}:{item['media_id']}")
if not item['data'].get('hide_dialogs', False):
self.progress.emit(f"Adding episode '{episode_name}' of '{podcast_name}' to download queue !")
Expand Down

0 comments on commit 7626e08

Please sign in to comment.