diff --git a/README.md b/README.md index d8c0c0c..ac30ffd 100755 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ If you have any questions or run into issues while using OnTheSpot, feel free to ## Contributing -If you encounter bugs, have suggestions, or would like to help translate the app to your native language feel free to [**open an issue**](https://github.com/justin025/onthespot/issues) or submit a pull request. +If you encounter bugs, have suggestions, or would like to help translate the app to your native language don't hesitate to [**open an issue**](https://github.com/justin025/onthespot/issues) or submit a pull request. ## Disclaimer diff --git a/docs/USAGE.md b/docs/USAGE.md index 8c7d669..ba752e2 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -1,9 +1,10 @@ # Usage Guide + ## 1. Logging into your accounts OnTheSpot supports various accounts and instructions for each are listed below, for further assistance please reach out for support on the community discord [here](https://discord.gg/GCQwRBFPk9). -- **Apple Music**: Enter the path to your Apple Music cookie in netscape format, the cookie can be deleted after the app has successfully logged in. To export your cookie you can use one of the following extensions, though I cannot verify that either are secure please use them at your own risk. +- **Apple Music**: Enter the path to your Apple Music cookie in netscape format, the cookie can be deleted after the app has successfully logged in. To export your cookie you can use one of the following extensions, please note that I have not verified either of the extensions listed are secure, please use them at your own risk. - Firefox Extension: [Export Cookies](https://addons.mozilla.org/en-CA/firefox/addon/export-cookies-txt/) - Chrome Extension: [Get cookies.txt Clean](https://chromewebstore.google.com/detail/get-cookiestxt-clean/ahmnmhfbokciafffnknlekllgcnafnie) @@ -14,6 +15,7 @@ OnTheSpot supports various accounts and instructions for each are listed below, - **Tidal**: The app will provide you a link, open the link and login in your browser. + ## 2. Searching and Downloading Music The search bar is able to parse queries, urls, and text files. @@ -23,6 +25,7 @@ If a url is provided the app will parse the url and immediately begin downloadin If a file path is provided the app will parse each line in the file for urls beginning in either http:// or https:// and begin downloading the items listed. + ## 4. Configuration ### General Configuration Options @@ -34,19 +37,19 @@ If a file path is provided the app will parse each line in the file for urls beg | ------ | ------ | | **Download Path** | Root folder where all downloaded media will be saved. | | **Theme** | Choose the application theme (`light` or `dark`). | -| **Download Buttons** | Adds extra functionalities like copying song links, opening tracks in your local music player, and locating the download directory. | +| **Download Buttons** | Adds extra functionalities to the download queue. | | **Show Thumbnails In Search/Downloads**| Display thumbnails on respective page. | | **Thumbnail Size**|Change the size of thumbnail icons. | | **Max Search Results** | Limits the number of search results displayed for each media type (e.g., songs, albums). | | **Explicit Label** | Customize how explicit content is labeled in file names and the app (default: 🅴). | | **Disable Bulk Download Notices** | Disables pop-up messages while downloading multiple songs or episodes. | -| **Mirror Spotify Playback** | Download currently playing song on the selected spotify account | +| **Mirror Spotify Playback** | Download currently playing song on the selected Spotify account | | **Windows 10 Explorer Thumbnails** | Embed thumbnails in a format that respects Windows 10 explorer and media player, this is an older format of ID3 and not widely supported. | | **Close To Tray** | Close application to tray on exit. | | **Check for Updates** | Automatically check for application updates. | | **File Bitrate** | Set the bitrate of a converted file, default value is 320k. This setting is not respected by lossless codecs, results may vary depending on your chosen filetype. | | **File Hertz** | Set the hertz of a converted file, default value is 44100 | -| **Track/Episode Format** | Select the audio format for your downloaded music or podcasts (e.g. `mp3`, `flac`, `ogg`, `m4a`). | +| **Track/Episode Format** | Select the file format to output your downloaded tracks or podcasts (e.g. `mp3`, `m4a`, `flac`, `ogg`, `wav`). For a complete list of supported codecs please see the following [list](https://ffmpeg.org/ffmpeg-formats.html). | | [**Track/Episode Path**](#trackplaylist-path-format) | Customize the file naming pattern for tracks, episodes, and playlists using variables like `{artist}`, `{album}`, etc. | | **Use Custom Playlist Path** | Enable the use of a custom path format for playlists. | | [**Playlist Path**](#trackplaylist-path-format) | Customize the file naming pattern for playlists using variables like `{artist}`, `{album}`, etc. | @@ -57,13 +60,13 @@ If a file path is provided the app will parse each line in the file for urls beg | **Save Album Cover** | Save album cover as an image with a default format of cover.png | | **Album Cover Format** | The image format to save album covers in (default: png) | | **Illegal Character Replacement** | Replace illegal characters in the filepath with the value specified (e.g., `/`, `\`, `<`, `>`, `*`, etc.). | -| **Download Lyrics\*** | Enable downloading of lyrics for each track/episode. *This feature requires a premium account.* | -| **Download Synced Lyrics Only\*** | Only download synced lyrics for tracks. *This feature requires a premium account.*| -| **Save LRC File\*** | Save lyrics in an `.lrc` file alongside the track. *This feature requires a premium account.* | +| **Download Lyrics\*** | Enable downloading of lyrics for each track/episode. *This feature may require a premium account.* | +| **Download Synced Lyrics Only\*** | Only download synced lyrics for tracks. *This feature may require a premium account.*| +| **Save LRC File\*** | Save lyrics in an `.lrc` file alongside the track. *This feature may require a premium account.*| | **Rotate Active Account** | Automatically rotate between added accounts for downloading to minimize the chance of hitting rate limits. | -| **Raw Media Download** | Downloads an unmodified file from whatever service is selected. With this enabled file conversion and the embedding of any metadata is skipped. | -| **Download Delay** | Time (in seconds) to wait before initiating the next download. Helps prevent Spotify's rate limits. | -| **Download Chunk Size** | The chunk size in which to download files. | +| **Raw Media Download** | Downloads an unmodified file from whatever service is selected. With this enabled file conversion and the embedding of any metadata is skipped. Lyrics and cover art will still be downloaded. | +| **Download Delay (s)** | The time,in seconds, to wait before initiating the next download. Helps prevent rate limits. | +| **Download Chunk Size (b)** | The chunk size, in bytes, in which to download files. | | **Maximum Queue Workers** | Set the maximum number of queue workers. Setting a higher number will queue songs faster, only change this setting if you know what you're doing. Changes to this setting require you to restart the app take effect. | | **Maximum Download Workers** | Set the maximum number of download workers. Only change this setting if you know what you're doing. Changes to this setting require you to restart the app to take effect. | | **Translate File Path** | Translate file paths into the application language. | @@ -109,7 +112,6 @@ If a file path is provided the app will parse each line in the file for urls beg > Setting the format to `{artist} - {name}.mp3` will result in files named like `Artist Name - Song Title.mp3`. - ## 6. Saving Your Configuration - **Apply Changes** diff --git a/src/onthespot/api/bandcamp.py b/src/onthespot/api/bandcamp.py index 8e65296..979b910 100644 --- a/src/onthespot/api/bandcamp.py +++ b/src/onthespot/api/bandcamp.py @@ -24,6 +24,7 @@ def bandcamp_login_user(account): }) return True except Exception as e: + logger.error(f"Unknown Exception: {str(e)}") account_pool.append({ "uuid": account['uuid'], "username": 'bandcamp', diff --git a/src/onthespot/api/deezer.py b/src/onthespot/api/deezer.py index a9d526d..de2b1f2 100644 --- a/src/onthespot/api/deezer.py +++ b/src/onthespot/api/deezer.py @@ -1,5 +1,6 @@ import html.parser import json +import random import re import requests import uuid @@ -220,9 +221,41 @@ def genurlkey(songid, md5origin, mediaver=4, fmt=1): def deezer_login_user(account): + uuid = account['uuid'] + arl = account['login']['arl'] + if uuid == 'public_deezer': + try: + # I have no idea why rentry 403s every scraping trick I've tried + ia_url = f"http://archive.org/wayback/available?url=https://rentry.co/firehawk52" + response = requests.get(ia_url) + if response.status_code != 200: + logger.error(f'Unable to fetch public deezer account from Internet Archive, status code: {response.raise_for_status()}') + raise Exception + data = response.json() + + # Sometimes returns with info missing + try: + url = data['archived_snapshots']['closest']['url'] + except Exception: + url = 'http://web.archive.org/web/20241206020314/https://rentry.co/firehawk52' + + html_content = requests.get(url).text + + table_match = re.search(r'(.*?)
', html_content, re.DOTALL) + if table_match: + table_content = table_match.group(1) + rows = re.findall(r'(.*?)', table_content, re.DOTALL) + + public_arls = [] + for row in rows[1:]: + public_arl = re.search(r'(.*?)', row) + public_arls.append(public_arl.group(1)) + arl = random.choice(public_arls) + except Exception as e: + logger.error('Failed to fetch firehawk52 shared deezer accounts.') + return False + try: - uuid = account['uuid'] - arl = account['login']['arl'] headers = { 'Origin': 'https://www.deezer.com', 'Accept-Encoding': 'utf-8', @@ -232,10 +265,6 @@ def deezer_login_user(account): session.headers.update(headers) session.cookies.update({'arl': arl, 'comeback': '1'}) - api_token = None - - # Prepare to call the API - method = 'deezer.getUserData' params = { 'api_version': "1.0", 'api_token': 'null', @@ -249,18 +278,24 @@ def deezer_login_user(account): headers=headers ).json() + account_type = 'free' bitrate = '128k' if user_data["results"]["USER"]["OPTIONS"]["web_lossless"]: + account_type = 'premium' bitrate = '1411k' elif user_data["results"]["USER"]["OPTIONS"]["web_hq"]: + account_type = 'premium' bitrate = '320k' + if uuid == 'public_deezer': + account_type = 'public' + account_pool.append({ "uuid": uuid, "username": arl, "service": "deezer", "status": "active", - "account_type": "premium" if user_data["results"]["USER"]["OPTIONS"]["web_lossless"] else "free", + "account_type": account_type, "bitrate": bitrate, "login": { "arl": arl, diff --git a/src/onthespot/api/soundcloud.py b/src/onthespot/api/soundcloud.py index b1ee786..a7adca1 100644 --- a/src/onthespot/api/soundcloud.py +++ b/src/onthespot/api/soundcloud.py @@ -89,7 +89,8 @@ def soundcloud_login_user(account): logger.info(f"Refreshed SoundCloud tokens as {client_id[:8]}******** {app_version[:8]}********") return True - except: + except Exception as e: + logger.error(f"Unknown Exception: {str(e)}") account_pool.append({ "uuid": "public_soundcloud", "username": "N/A", diff --git a/src/onthespot/api/tidal.py b/src/onthespot/api/tidal.py index 61c9107..89ba4c9 100644 --- a/src/onthespot/api/tidal.py +++ b/src/onthespot/api/tidal.py @@ -87,7 +87,8 @@ def tidal_login_user(account): response = requests.post(f"{AUTH_URL}/token", data=data, auth=AUTH) if response.status_code != 200: - logger.info(f"Token refresh pending: {response.json()}") + logger.info(f"Error user's ip address is likely blocked, status code: {response.status_code}") + raise Exception auth_data = response.json() @@ -114,7 +115,7 @@ def tidal_login_user(account): }) return True except Exception as e: - logger.error(f"Unknown Exception: {str(e)}") + logger.error(f"Tidal Login Error: {str(e)}") account_pool.append({ "uuid": account['uuid'], "username": account['login']['username'], diff --git a/src/onthespot/api/youtube.py b/src/onthespot/api/youtube.py index fdb1edb..bc0c366 100644 --- a/src/onthespot/api/youtube.py +++ b/src/onthespot/api/youtube.py @@ -106,6 +106,19 @@ def youtube_get_track_metadata(_, item_id): else: length = '' + # Get thumbnail url + thumbnail_url = None + for thumbnail in info_dict.get('thumbnails', []): + current_url = thumbnail.get('url', '') + # Square thumbnails are stored on googleusercontent.com as + # opposed to ytimg.com. Select the last (highest quality) + # square thumbnail. + if 'googleusercontent.com' in current_url: + thumbnail_url = current_url + + if not thumbnail_url and info_dict.get('thumbnails', []): + thumbnail_url = info_dict.get('thumbnails', [])[-1].get('url', '') + info = {} info['title'] = info_dict.get('title', '') album = info_dict.get('album', '') @@ -116,7 +129,8 @@ def youtube_get_track_metadata(_, item_id): # Commented thumbnails are periodically missing #info['image_url'] = info_dict.get('thumbnail', '') #info['image_url'] = f'https://i.ytimg.com/vi/{item_id}/maxresdefault.jpg' - info['image_url'] = f'https://i.ytimg.com/vi/{item_id}/hqdefault.jpg' + #info['image_url'] = f'https://i.ytimg.com/vi/{item_id}/hqdefault.jpg' + info['image_url'] = thumbnail_url info['language'] = info_dict.get('language', '') info['item_url'] = url # Windows takes issue with the following line, not sure why diff --git a/src/onthespot/gui/qtui/main.ui b/src/onthespot/gui/qtui/main.ui index 9053814..f1d1e47 100644 --- a/src/onthespot/gui/qtui/main.ui +++ b/src/onthespot/gui/qtui/main.ui @@ -656,7 +656,7 @@ 0 - 0 + -1543 627 3255 @@ -2201,7 +2201,7 @@ - Download Delay + Download Delay (s) @@ -2249,7 +2249,7 @@ - Download Chunk Size + Download Chunk Size (b) diff --git a/src/onthespot/otsconfig.py b/src/onthespot/otsconfig.py index 7f91edd..9ec27f4 100755 --- a/src/onthespot/otsconfig.py +++ b/src/onthespot/otsconfig.py @@ -138,6 +138,14 @@ def __init__(self, cfg_path=None): "service": "bandcamp", "active": True, }, + { + "uuid": "public_deezer", + "service": "deezer", + "active": True, + "login": { + "arl": "public_deezer", + } + }, { "uuid": "public_soundcloud", "service": "soundcloud",