Skip to content

Commit

Permalink
Changed directory layout, improve packaging
Browse files Browse the repository at this point in the history
- Changed directory layout to add support for setup tools
- Added application icons and file desktop entry
- Sanitised call to ffmpeg binary allowing tracks with shell special characters like '$' to be downloaded
- Add support for user defined separator for metadata items with multiple values
- Windows portable binaries now build without console window
- Added descriptions of config variables in otsconfig.py
- Updated README.md
- Improved integration with Linux desktop environments
  • Loading branch information
casualsnek committed Apr 29, 2023
1 parent 7626e08 commit e2fbb18
Show file tree
Hide file tree
Showing 30 changed files with 203 additions and 116 deletions.
83 changes: 57 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,55 @@
![Logo](src/onthespot/resources/icon.png)

# Onthespot

qt based music downloader written in python.

![Screenshot](https://i.imgur.com/C4V94gy.png)

### Discord for discussions: [Discord Invite](https://discord.gg/2t6BNVgZ)
If you have an idea for an improvement or feature, create a issue or join the discord server to discuss!

# 1. Installing/launching application:
## 1.1. From source
## 1.1. Launch without installing - from source
Make sure [python3](https://www.python.org/downloads) and [Git](https://git-scm.com/downloads) are installed on your system, if you are on windows you also need to install Microsoft C++ build tools from [HERE](https://visualstudio.microsoft.com/visual-cpp-build-tools/) and restart your computer before starting build process.
- Download or Clone the repo ```git clone https://github.com/casualsnek/onthespot```
- Navigate to the onthespot directory ```cd onthespot```
- Install the dependencies with ```pip install -r requirements.txt```
- Launch the application with ```python3 onthespot.py```
- Navigate to source directory ```cd src```
- Launch the application with ```python3 -m onthespot```

*Windows users should also follow these extra steps before running:* ```python3 onthespot.py```
```
pip install winsdk
```

## 1.2. Using prebuilt binaries
## 1.2. Using portable prebuilt binaries
### On Linux
Download Latest 'onthespot_linux' from the release section and execute with
```
chmod +x onthespot_linux
./onethespot_linux
```
### On Windows
Download Latest 'onthespot_win_ffm.exe' or 'onthespot_win.exe' from the Release section and execute by double clicking the downloaded file.
Download Latest 'onthespot_win_ffm.exe' or 'onthespot_win.exe' from the Release section and execute by double-clicking the downloaded file.

The binaries with filename ending with '_ffm' have ffmpeg bundled and should not require manual installation.

If you are using binaries that does not bundle ffmpeg and downloads gets stuck at 99% with ```Converting``` on progress text, you are missing ffmpeg ! Please install it by following the instructions below

#### Installing ffmpeg in windows
- Open Windows Explorer and Navigate to ```C:\``` Drive and make a folder name ```ffmpeg``` there
- Download ffmpef zip from [https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z](https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z) then copy the ``bin`` folder from zip to ```C:\ffmpeg```
- Download ffmpeg zip from [https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z](https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z) then copy the ``bin`` folder from zip to ```C:\ffmpeg```
- Open CMD as administrator and run the command: ```setx /m PATH "C:\ffmpeg\bin;%PATH%"```

Now the application should work as expected.


# 2. Building manually
Building on any OS requires Git, Python3 and Pip installed. Make sure you have them installed !
# 2. Building/packaging manually
Building or packaging on any OS requires Git, Python3 and Pip installed. Make sure you have them installed !

## 2.1. On Linux/nix
## 2.1. Building portable binaries
### 2.1.1 On Linux/nix
Open terminal emulator and run the following command to clone the repository and build.
```bash
git clone https://github.com/casualsnek/onthespot
Expand All @@ -56,7 +62,7 @@ bash ./build_linux.sh
```
After the command completes, you should have a 'dist' directory in repository root containing built 'onthespot_linux' binary.

## 2.2. On Windows
### 2.1.2. On Windows

Open cmd and run the following command to clone the repository and build.
```cmd
Expand All @@ -72,20 +78,36 @@ build_winC2.bat
```
After the command completes, you should have a 'dist' directory in repository root containing built 'onthespot_win.exe' binary.

## 2.2. Building wheel for installing with pip
You can also build onthespot as wheel and install it as python module via pip in your system. It provides better integration with system, like using your system's Qt style and themes as well as you can use provided icon and .desktop file for better integration under linux systems.

Make sure you have set up tools installed !

Open terminal emulator and run the following command to clone the repository and build.
```bash
git clone https://github.com/casualsnek/onthespot
cd onthespot
python -m build
```
This will create a dist directory containing .whl file that can now be installed with pip, the application can be launched with the command ```onthespot_gui``` or ```python3 -m onthespot``` after installing !

**NOTE :** If you are packaging onthespot for distribution, copy ```src/onthespot/resources/icon.svg``` to either ```/usr/share/icons/hicolor/scalable/apps/casual_onthespot.svg``` or ```$HOME/.local/share/icons/hicolor/scalable/apps/casual_onthespot.svg```, and ``` src/onthespot/resources/org.eu.casualsnek.onthespot.desktop``` to either ```/usr/share/applications/org.eu.casualsnek.onthespot.desktop``` or ```$HOME/.local/share/applications/org.eu.casualsnek.onthespot.desktop```. This allows application to be better integrated to desktop environments !

If you have ideas for improvement/features create a issue or join discord server for discussion !
<br>
If you have ideas for improvement/features create an issue or join discord server for discussion !

# 3. Basic Usage
## Getting started
On your first launch of application you will get a warning that no spotify accounts are added, click ok and add your account(s) at the bottom of the application. After adding your accounts, close and restart the application. Having multiple accounts will let you download multiple songs at a time.

## Searching/Downloading via query
Click on 'Search' tab then enter your query and cick search to search for songs/artists/albums/playlists and click download.
Click on 'Search' tab then enter your query and click search to search for songs/artists/albums/playlists and click download.
You can download any media like 'Albums', 'Tracks', etc., that appear on the results table all at once by using the download buttons below the results table.
*Note that Media Type other than 'Tracks' can take a little longer to parse and download. The application may appear to be frozen in this state !*

## Downloading by URL
Enter the url in the url field then click download.
Enter the url in the search field then click download.
You can also enter path of text file containing URL, and it will queue all url(s) in it !
*Note that Media Type other than 'Tracks' can take a little longer to parse and download. Application may appear to be frozen in this state !*

## Download status
Expand All @@ -111,12 +133,20 @@ Default track names are ```AlbumFormatter/TrackName```
- **Track name formatter**:
This option allows you to set the naming scheme of downloaded tracks.
Variables can be used by enclosing them between `{}`. A few variables are available to use in the naming scheme:
- artist : name of artist of track
- album : name of album the track is in *
- name : name of track
- rel_year : release year of track
- disc_number : disk number in which track lies *
- track_number : serial Number of track in album *
- artist : Name of artist of track
- album : Name of album the track is in *
- name : Name of track
- rel_year : Release year of track
- disc_number : Disk number in which track lies *
- track_number : Serial Number of track in album *
- playlist_name : Name of playlist if the track is being downloaded as part of playlist *
- playlist_owner : Name of playlist if the track is being downloaded as part of playlist *
- playlist_desc : Description of playlist if the track is being downloaded as part of playlist *
- genre : Genre of song *
- label : Name of record label
- explicit : 'Explicit' if the song is marked explicit else it will be blank
- trackcount : Total number of tracks on the album this track is in
- disccount : Total number of discs on the album this track is in
- spotid : Spotify ID
- Example: ```Song: {name} of album: {album} Released in {rel_year}```.
The value of variables with their description ending in * maybe empty in some cases. This can also be a path.
Expand All @@ -127,8 +157,13 @@ Variables can be used by enclosing them between `{}`. A few variables are availa
- artist : name of the main artist of the album
- rel_year: the release year of the album *
- album: name of the album
- playlist_name : Name of playlist if the track is being downloaded as part of playlist *
- playlist_owner : Name of playlist if the track is being downloaded as part of playlist *
- playlist_desc : Description of playlist if the track is being downloaded as part of playlist *
- genre : Genre of song *
- label : Name of record label
- Example: ```{artist}/{rel_year}/{album}```.
The value of variables with their description ending in * maybe empty in some cases. This can be a path too.
The value of variables with their description ending in * maybe empty in some cases. This can be a path too.

- **Download chunk size**:
Size of chunks (bytes) used for downloading.
Expand All @@ -141,11 +176,11 @@ Time to wait before attempting another download after failed attempt.

- **Skip bytes at the end (download end skip bytes)**:
Sometimes the last few bytes of a track can't be downloaded which causes 'PD Error' to show up which causes downloads to fail constantly, this sets the number of bytes to skip downloading if this happens.
The value might change but the current working vaue is '167' bytes. If you get 'decode errors' or incomplete song downloads try setting it to 0.
The value might change but the current working value is '167' bytes. If you get 'decode errors' or incomplete song downloads try setting it to 0.

- **Force Artist/Album dir for track/playlist items**:
If this is disabled the tracks downloaded will be placed in the root of download directory instead of artist/album directories.
Enabling this might cause slower download parsing but makes orgainsing music easier.
Enabling this might cause slower download parsing but makes organising music easier.

- **Media Format**:
Format of media you want your final music download to be in.
Expand All @@ -154,12 +189,8 @@ Do not include '.' in it. This setting will be ignored while using the raw media
# 5. Issues
Decode error: If you are receiving this error, your account might have got restricted. Wait some time or try a different account. The application may crash frequently as there is no proper exception handling yet. You can help by opening a new issue with the error message displayed in your console window after the application misbehaves.

# 6. TODOS
- Synced lyrics downloads
- Improve UI/UX ( Suggestions needed by users )
- Refactor code

# 7. Contributing/Supporting
# 6. Contributing/Supporting
You can write code to include additional feature or bug fixes or make a issue regarding bugs and features or just spread the work about the application :)
If you want to support financially, you can visit [Here](https://github.com/casualsnek/casualsnek) and support through open collective or BTC
If you like the project, show your support by giving it a star :) !
23 changes: 12 additions & 11 deletions build_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,28 @@ pip install -r requirements.txt
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="." \
--add-data="src/onthespot/gui/qtui/*.ui:onthespot/gui/qtui" \
--add-data="src/onthespot/resources/*.png:onthespot/resources" \
--add-binary="ffbin_nix/*:onthespot/bin/ffmpeg" \
--paths="src/onthespot" \
--name="onthespot_linux_ffm" \
--icon="resources/icon.png" \
__init__.py
--icon="src/onthespot/resources/icon.png" \
src/portable.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="." \
--add-data="src/onthespot/gui/qtui/*.ui:onthespot/gui/qtui" \
--add-data="src/onthespot/resources/*.png:onthespot/resources" \
--paths="src/onthespot" \
--name="onthespot_linux" \
--icon="resources/icon.png" \
__init__.py
--icon="src/onthespot/resources/icon.png" \
src/portable.py
fi
echo " => Setting permissions.. "
chmod +x ./dist/onthespot_linux
echo " => Cleaning.. "
rm onthespot_linux.spec
rm onthespot_linux_ffm.spec
rm -rf ./build
rm -rf __pycache__
echo " => Done "
5 changes: 3 additions & 2 deletions build_winC2.bat
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ 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-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
pyinstaller --noconsole --onefile --noconfirm --add-data="src/onthespot/gui/qtui/*.ui;onthespot/gui/qtui" --add-data="src/onthespot/resources/*.png;onthespot/resources" --add-binary="ffbin_win/*.exe;onthespot/bin/ffmpeg" --paths="src/onthespot" --name="onthespot_win_ffm" --icon="src/onthespot/resources/icon.png" src\portable.py
) else (
echo =^> Building to use ffmpeg binary from system...
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
pyinstaller --noconsole --onefile --noconfirm --add-data="src/onthespot/gui/qtui/*.ui;onthespot/gui/qtui" --add-data="src/onthespot/resources/*.png;onthespot/resources" --paths="src/onthespot" --name="onthespot_win" --icon="src/onthespot/resources/icon.png" src\portable.py
)
echo =^> Cleaning..
del /F /Q /A onthespot_win.spec
del /F /Q /A onthespot_win_ffm.spec
rmdir build /s /q
rmdir __pycache__ /s /q
echo =^> Done
32 changes: 28 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,41 @@ license = GPL-2.0 License

[options]
package_dir=
=.
=src
python_requires = >= 3.0
packages = find:
include_package_data = True
install_requires =
async-timeout==4.0.2
certifi==2022.12.7
charset-normalizer==3.1.0
defusedxml==0.7.1
idna==3.4
ifaddr==0.2.0
librespot==0.0.8
music-tag==0.4.3
mutagen==1.46.0
packaging==23.1
Pillow==9.5.0
protobuf==3.20.1
pycryptodomex==3.17
PyOgg==0.6.14a1
PyQt5==5.15.9
PyQt5-Qt5==5.15.2
PyQt5-sip==12.12.1
PyQt5-stubs==5.15.6.0
pyxdg==0.28
requests==2.28.2
show-in-file-manager==1.1.4
urllib3==1.26.15
websocket-client==1.5.1
zeroconf==0.55.0

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

[options.packages.find]
where=.
where=src

[options.package_data]
* = *.ui, *.png
* = *.ui, *.png, *.desktop
6 changes: 3 additions & 3 deletions __init__.py → src/onthespot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
import sys
from PyQt5.QtWidgets import QApplication
from gui.mainui import MainWindow
from gui.minidialog import MiniDialog
from runtimedata import get_logger
from .gui.mainui import MainWindow
from .gui.minidialog import MiniDialog
from .runtimedata import get_logger


def main():
Expand Down
4 changes: 4 additions & 0 deletions src/onthespot/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import main

if __name__ == '__main__':
main()
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
from PyQt5.QtWidgets import QHBoxLayout, QWidget
from runtimedata import downloaded_data, cancel_list, failed_downloads, downloads_status, download_queue
from ..runtimedata import downloaded_data, cancel_list, failed_downloads, downloads_status, download_queue
from showinfm import show_in_file_manager


Expand Down
12 changes: 6 additions & 6 deletions gui/mainui.py → src/onthespot/gui/mainui.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
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
from utils.spotify import search_by_term, get_thumbnail
from utils.utils import name_by_from_sdata, login_user, remove_user, get_url_data, re_init_session
from worker import LoadSessions, ParsingQueueProcessor, MediaWatcher, PlayListMaker, DownloadWorker
from ..exceptions import EmptySearchResultException
from ..utils.spotify import search_by_term, get_thumbnail
from ..utils.utils import name_by_from_sdata, login_user, remove_user, get_url_data, re_init_session
from ..worker import LoadSessions, ParsingQueueProcessor, MediaWatcher, PlayListMaker, DownloadWorker
from .dl_progressbtn import DownloadActionsButtons
from .minidialog import MiniDialog
from otsconfig import config
from runtimedata import get_logger, download_queue, downloads_status, downloaded_data, failed_downloads, cancel_list, \
from ..otsconfig import config
from ..runtimedata import get_logger, download_queue, downloads_status, downloaded_data, failed_downloads, cancel_list, \
session_pool, thread_pool
from .thumb_listitem import LabelWithThumb
from urllib3.exceptions import MaxRetryError, NewConnectionError
Expand Down
2 changes: 1 addition & 1 deletion gui/minidialog.py → src/onthespot/gui/minidialog.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
from PyQt5 import uic
from PyQt5.QtWidgets import QDialog
from runtimedata import get_logger
from ..runtimedata import get_logger

logger = get_logger('gui.minidialog')

Expand Down
1 change: 1 addition & 0 deletions src/onthespot/gui/qtui/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit e2fbb18

Please sign in to comment.