Spodcast is a caching Spotify podcast to RSS proxy

Overview

Spodcast

Spodcast is a caching Spotify podcast to RSS proxy. Using Spodcast you can follow Spotify-hosted netcasts/podcasts using any player which supports RSS, thus enabling the use of older hardware which is not compatible with the Spotify (web) app. Spodcast consists of the main Spodcast application - a Python 3 command line tool - and a PHP-based RSS feed generator. It uses the librespot-python library to access the Spotift API. To use Spodcast you need a (free) Spotify account. Spodcast only supports the Spotify podcast service, it does not interface with the music streaming service.

How does it work

Spotify hosts podcasts through their proprietary API and does not offer an RSS feed, making it mandatory to use the Spotify (web) app to follow these shows. This makes it impossible to follow Spotify-hosted shows on any device which does not support the Spotify (web) app. Spodcast solves this problem by creating an RSS feed out of data returned through the Spotify podcast API. This feed can be served by any web server which supports PHP. By running Spodcast through a task scheduler (cron on *nix, Task Scheduler on Windows) the feed will be kept up to date without the need for intervention. Have a look at these glorious ASCIIGraphs™ which should answer all unasked questions:

Spodcast regularly queries Spotify for new episodes...

                  --------------
                 |task scheduler|
                  --------------
                        |             ___________
  -------   APIv1   ----V---         /           \
 |Spotify|- - - - >|Spodcast|------>| File system |
  -------           --------         \___________/

You want to listen to an episode using your old, unsupported but still functional phone...

                                           _____         ............
   ___________          ----------        |     | . o O |bla bla bla.|
  /           \        |Web server|  RSS  | YOUR|        ````````````
 | File system |------>|  + PHP   |------>| OLD |
  \___________/         ----------        |PHONE|
                                          |_____|

Thus, by the simple expedient of using a piece of code which produces another piece of code which is used by yet another piece of code to speak to that old, creaky but still functional phone the latter is saved from early forced retirement. You can both feel virtuous for not adding another piece of waste to the pile, provident for not spending funds on a new device which does the same as the old one, smart for escaping the trap of planned obsolescence and whatever other emotion you prefer, including none whatsover.

Installation

Spodcast can be installed from source by running pip install . from within the package root directory:

$ git clone https://github.com/Yetangitu/spodcast.git
$ cd spodcast
$ pip install .

Once installed this way it can be uninstalled using pip uninstall spodcast if so required. If you're planning to use the RSS proxy and web UI you need to make sure the spodcast command is available to the web server user.

Usage

To use Spodcast you need a (free) Spotify account, if you don't have one yet you'll need to take care of that first at https://www.spotify.com/signup/ . If you plan to use the RSS proxy feature you'll also need a web server to serve the RSS feed(s), any server which supports PHP will do here. See Web server requirements for more information on how to configure the server.

Here's spodcast displaying its help message:

$ spodcast -h
usage: spodcast [-h] [-c CONFIG_LOCATION] [-p] [-l LOGIN] [--root-path ROOT_PATH]
                [--skip-existing SKIP_EXISTING] [--retry RETRY]
                [--max-episodes MAX_EPISODES] [--chunk-size CHUNK_SIZE]
                [--download-real-time DOWNLOAD_REAL_TIME] [--language LANGUAGE]
                [--credentials-location CREDENTIALS_LOCATION] [--rss RSS]
                [--log-level LOG_LEVEL]
                [urls ...]

A caching _Spotify_ podcast to RSS proxy.

positional arguments:
  urls                  Download podcast episode(s) from a url. Can take multiple urls.

optional arguments:
  -h, --help            show this help message and exit
  -c CONFIG_LOCATION, --config-location CONFIG_LOCATION
                        Specify the spodcast.json location
  -p, --prepare-feed    Installs RSS feed server code in ROOT_PATH.
  -l LOGIN, --login LOGIN
                        Reads username and password from file passed as argument and stores
                        credentials for later use.
  --root-path ROOT_PATH
                        set root path for podcast cache
  --skip-existing SKIP_EXISTING
                        skip files with the same name and size
  --retry RETRY         retry count for _Spotify_ API access
  --max-episodes MAX_EPISODES
                        number of episodes to download
  --chunk-size CHUNK_SIZE
                        download chunk size
  --download-real-time DOWNLOAD_REAL_TIME
                        simulate streaming client
  --language LANGUAGE   preferred content language
  --credentials-location CREDENTIALS_LOCATION
                        path to credentials file
  --rss RSS             add a (php) RSS feed server and related metadata for feed. To serve
                        the feed, point a web server at the spodcast root path as configured
                        using --root-path.
  --log-level LOG_LEVEL
                        log level (debug/info/warning/error/critical)

Using Spodcast to proxy Spotify podcasts to RSS

The following example shows how to use the spodcast command to prepare the feed root directory and add a Spotify account to be used. It specifies the configuration file to create (-c /mnt/audio/podcast/spodcast.json) and the root path where podcasts will be downloaded to (--root-path /mnt/audio/spodcast). The -p option tells spodcast to prepare the RSS feed server in the root directory which will also be used to store the credential file created by the -l spotify.rc command. That spotify.rc file is a plain text file containing the username and password (separated by a single space character) to use to login to Spotify. It is only needed to create the stored credentials file(s) so it can be deleted once Spotcast is up and running.

spodcast -c /mnt/audio/podcast/spodcast.json --root-path /mnt/audio/spodcast -p -l /home/exampleuser/spotify.rc

Configure the Web server using the path given as root path (in this example that would be /mnt/audio/spodcast) as web root, making sure to exclude files with .json and .info extenstions to avoid leaking your Spotify credentials (even though these are stored in hashed form using hashed file names). Now point a browser at the site you configured for Spodcast and you're ready to add the first show or episode. This is done easily by entering the Spotify show/episode url (e.g. https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk for The Joe Rogan Experience for the whole show, https://open.spotify.com/episode/2rYwwE7hcpgsDo9vRVHxAI?si=24fb00294b7f40db for a specific episode, notice the show and episode parts of these links) and either hitting Enter or clicking the Add button. Spodcast will now create a directory under the given root path, add the .index.php RSS feed generator script and the index.info show info URL used by that script and the RSS manager script and whatever episode(s) you decided to sync.

Once the initial feed has been created it can be kept up to date by enabling the feed update service found in the Settings menu. Select the update frequency and the start time and click Update, this will create a cron job for the web server which will run the Spodcast manager script to update feeds. While the update frequency is configured for all shows simultaneously this is not the case for the number of episodes to sync and the number to keep in cache, these can be configured individually for each show. The idea here is that some shows may publish more than one episode between update intervals so by fetching the last X episodes on each update nothing will be missed. Episodes which have already been synced will not be synced again so no time or bandwidth is wasted. In the same vein the number of episodes to keep can be configured to make sure your RSS clients have the opportunity to download these before they are rotated out of cache. Once more than X (being the value chosen for keep) episodes have been downloaded the oldest episodes will be deleted to keep the total no more than X.

Point your RSS clients at the Spodcast feed URL for this show and you should see new episodes appear after they were published on Spotify and subsequently picked up on the next update. For the example given in the Web server requirements example that URL would be http://spodcast.example.org/The_Joe_Rogan_Experience.

Here's what the Spodcast feed manager looks like:

Spodcast feed manager

...and on smaller screens it looks like this:

Spodcast feed manager on a small screen

The settings screen is simple and concise:

Spodcast feed manager settings

Each show has its own sync and keep settings. Use the Delete button to, well, delete the show. Use Refresh to retrieve the last [sync] episodes, skipping those which have already been synced.

Show controls

Using the Spodcast CLI command to download a single episode

Spodcast can also be used stand-alone (without the need for a web server) by either just ignoring the feed-related files (.index.php, index.info plus a *.info file for every episode) or by disabling the RSS feed using --rss no on the command line. Instead of using the -l spotify.rc command to add Spotify credentials it is possible to point Spotcast at a single credentials.json file (which will be created if it does not exist yet), spotcastwil ask for the username and password when needed. To get single episode links use the _Spotify_ web app and select _Share->Copy Episode Link_ from the episode menu (three dots in the top-right corner of the episode block). The following example shows (an already configured instance of)spodcast` ready to download a single episode:

spodcast -c ~/.config/spodcast/spodcast.json --credentials-location ~/.config/spodcast/credentials.json --rss no https://open.spotify.com/episode/2rYwwE7hcpgsDo9vRVHxAI?si=24fb00294b7f40db

Like in the previous example Spodcast will create a directory under the root path with the same name as the show from which the episode is downloaded. The episode will be downloaded into this directory under a SHOW_NAME_-__EPISODE_NAME.[ogg|mp3] name. Point a mediaplayer of choice at this file to play the episode. In "manual" mode Spodcast does not do anything by itself, feeds can be kept up to date by running Spotcast with the required settings for --max-episodes (which is the value used for sync in the web UI) and the show URL. Here's how to update the The Joe Rogan Experience show using the spodcast CLI command, syncing the last 3 episodes:

spodcast -c ~/.config/spodcast/spodcast.json --rss no --max-episodes 3 https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk`

Web server configuration

Spodcast places a hidden .index.php file in the root path and each show directory. The one in the root directory is used to manage feeds while those in the show directories produce RSS feeds based on the information found in all *.info files in that directory. Configure the server to serve those .index.php files as index to make things work as intended. Don't forget to block all web access to files ending in .json and .info to make sure you Spotify credentials (which are stored in hashed form in files named spodcast-cred-MD5_HASH_OF_SPOTIFY_USER_NAME.json in the root path) can not be accessed. For nginx the following should suffice to produce an unencrypted (HTTP) feed under the domain name spodcast.example.org given a feed root directory (as configured using --root-path) of /mnt/audio/spodcast with php-fpm 7.4 listening on unix:/run/php/php7.4-fpm.sock:

server {
        listen 80;
        listen [::]:80;
        server_name spodcast.example.org;

        root /mnt/audio/spodcast;

        index .index.php;

        # these files should not be accessible
        location ~\.(json|info)$ {
                deny all;
                return 404;
        }

        location / {
                try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.4-fpm.sock;
        }
}

Examples for other web servers can be found elsewhere, this is basically a default PHP configuration with the only difference being that .index.php is a hidden file.

Comments
  • Cannot get webserver to run properly

    Cannot get webserver to run properly

    This tool is awesome, thank you. But i am having a hard setting it up properly.

    I got the Web-Interface running, but i cannot add podcasts through it (credentials set, no error message). When i add a podcast through the CLI the podcast shows up on the main page, but the served .index for the podcast is empty.

    Can you write a small tutorial how to set up the webserver or point me in the right direction?

    I am using a RPi 4 with 64-bit bullseye and nginx, php-fpm.

    bug 
    opened by ahuse 20
  • Cannot download episodes anymore (Not found for url exception)

    Cannot download episodes anymore (Not found for url exception)

    Hi,

    somehow I cannot download anymore episodes.

    The full log:

    DEBUG:spodcast.spodcast:args: Namespace(config_location='/mnt/audio/spodcast/spodcast.json', prepare_feed=False, urls=['https://open.spotify.com/show/1OLcQdw2PFDPG1jo3s0wbp'], login=None, root_path='/mnt/audio/spodcast', skip_existing=None, retry=None, max_episodes='5', chunk_size=None, download_real_time=None, language=None, credentials_location=None, rss_feed=None, transcode=None, log_level='debug', func=<function client at 0xb6b07fa0>) DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): apresolve.spotify.com:443 DEBUG:urllib3.connectionpool:https://apresolve.spotify.com:443 "GET /?type=accesspoint HTTP/1.1" 200 None INFO:Librespot:Session:Created new session! device_id: xxxxxxxxxxxxxxxxxxxxxxx, ap: ap-gew1.spotify.com:4070 INFO:Librespot:Session:Connection successfully! INFO:Librespot:Session:Session.Receiver started DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): apresolve.spotify.com:443 DEBUG:urllib3.connectionpool:https://apresolve.spotify.com:443 "GET /?type=spclient HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): apresolve.spotify.com:443 INFO:Librespot:Session:Skipping 02 INFO:Librespot:Session:Received license_version: 0 INFO:Librespot:Session:Received country_code: DE DEBUG:Librespot:Session:Parsed product info: {'type': 'open', 'ab-ad-player-targeting': '1', 'ab-ad-requester': '1', 'ab-android-push-notifications': '1', 'ab-browse-music-tuesday': '1', 'ab-collection-bookmark-model': '1', 'ab-collection-followed-artists-only': '1', 'ab-collection-hide-unavailable-albums': '0', 'ab-collection-offline-mode': '0', 'ab-collection-union': '1', 'ab-desktop-hide-follow': '0', 'ab-desktop-playlist-annotation-edit': '1', 'ab-mobile-discover': '0', 'ab-mobile-running-onlymanualmode': 'only-manual', 'ab-mobile-running-tempo-detection': 'Control', 'ab-mobile-social-feed': '1', 'ab-mobile-startpage': '0', 'ab-moments-experience': '0', 'ab-new-share-flow': '0', 'ab-play-history': '0', 'ab-playlist-extender': '5', 'ab-sugarpills-sanity-check': '2', 'ab-test-group': '767', 'ab-watch-now': '0', 'ab_recently_played_feature_time_filter_threshold': 'com.spotify.gaia=30,driving-mode=120,spotify%3Ainternal%3Astartpage=30', 'ad-catalogues': 'spotify', 'ad-formats-preroll-video': '0', 'ad-formats-video-takeover': '1', 'ad-persist-reward-time': '0', 'ad-session-persistence': '1', 'ad-use-adlogic': 'stream', 'ads': '1', 'allow-override-internal-prefs': '0', 'ap-resolve-pods': '0', 'app-developer': '0', 'arsenal_country': '1', 'audio-preview-url-template': 'https://p.scdn.co/mp3-preview/{id}', 'backend-advised-bitrate': '1', 'browse-overview-enabled': '1', 'buffering-strategy': '0', 'buffering-strategy-parameters': '0.8:0.2:0.0:0.0:0.0:0.0:1.0:10:10:2000:10000:10485760', 'capper-profile': None, 'capping-bar-threshold': '3601', 'catalogue': 'free', 'collection': '1', 'enable-annotations': '2', 'enable-annotations-read': '0', 'enable-autostart': '1', 'enable-crossfade': '1', 'enable-gapless': '1', 'expiry': '1', 'explicit-content': '1', 'fb-grant-permission-local-render': '0', 'fb-info-confirmation': 'control', 'financial-product': 'pr:open,tc:0', 'head-file-caching': '1', 'head-files': '1', 'head-files-url': 'https://heads-fa.scdn.co/head/{file_id}', 'high-bitrate': '0', 'image-url': 'https://i.scdn.co/image/{file_id}', 'incognito_mode_timeout': '21600', 'india-experience': '0', 'instant-search': '0', 'instant-search-expand-sidebar': '0', 'is_email_verified': '0', 'key-caching-max-count': '10000', 'key-caching-max-offline-seconds': '1800', 'key-memory-cache-mode': '1:15,300', 'lastfm-session': None, 'libspotify': '0', 'license-acceptance-grace-days': '30', 'license-agreements': None, 'local-files-import': '0', 'metadata-link-lookup-modes': '0', 'mobile': '0', 'mobile-browse': '0', 'mobile-login': '0', 'mobile-payment': '0', 'name': 'Spotify Free', 'network-operator-premium-activation': '1', 'nft-disabled': '1', 'npt-disabled': '2', 'offline': '0', 'on-demand': '1', 'pause-after': '18000', 'payments-locked-state': '0', 'player-license': 'on-demand', 'playlist-annotations-markup': '0', 'playlist-folders': '1', 'preferred-locale': 'en', 'prefetch-keys': '1', 'prefetch-strategy': '0', 'prefetch-window-max': '2', 'profile-image-upload': '1', 'public-toplist': '0', 'publish-activity': '0', 'publish-playlist': '0', 'radio': '1', 'remote-control': '6', 'send-email': '0', 'shows-collection': '1', 'shows-collection-jam': '1', 'shuffle': '0', 'shuffle-algorithm': '1', 'sidebar-navigation-enabled': '0', 'storage-size-config': '10240,90,500,3', 'streaming': '1', 'streaming-rules': None, 'track-cap': '0', 'ugc-abuse-report': '1', 'ugc-abuse-report-url': 'https://support.spotify.com/abuse/?uri={uri}', 'use-fb-publish-backend': '2', 'use-pl3': '0', 'use-playlist-app': '0', 'use-playlist-uris': '0', 'user-profile-show-invitation-codes': '0', 'video-cdn-sampling': '1', 'video-device-blacklisted': '0', 'video-initial-bitrate': '200000', 'video-keyframe-url': 'http://keyframes-fa.cdn.spotify.com/keyframes/v1/sources/{source_id}/keyframe/heights/{height}/timestamps/{timestamp_ms}.jpg', 'video-manifest-url': 'https://spclient.wg.spotify.com/manifests/v6/{type}/sources/{source_id}/options/supports_drm', 'video-wifi-initial-bitrate': '800000', 'wanted-licenses': None, 'widevine-license-url': 'https://spclient.wg.spotify.com/widevine-license/v1/video/license'} INFO:Librespot:Session:Skipping 69 INFO:Librespot:Session:Skipping 1f DEBUG:Librespot:MercuryClient:Handling packet, cmd: 0xb5, seq: 331586318658174976, flags: b'\x01', parts: 1 DEBUG:Librespot:MercuryClient:Couldn't dispatch Mercury event seq: 331586318658174976, uri: hm://pusher/v1/connections/ZjU4MmIxNmNiODE1NWM3YmMxZjgxMDhhMTZmNjRjZjFjODRjMTA5YytBUCt0Y3A6Ly9nZXcxLWFjY2Vzc3BvaW50LWEtZ2Q0OS5nZXcxLnNwb3RpZnkubmV0OjUwMDMrRDEzMDNCNkMwQ0REN0EyOTA3MTg5QzdENjJFOTE4QkNDNjc5RjlBMTJEN0UwNDE3RTZCQjI3NTA1QjcyMDFDNQ%3D%3D, code: 200, payload: b'' DEBUG:Librespot:Session:Received 0x10: 71ecf06a1db7dd2e153baadbd9bdebe8c0626907 INFO:Librespot:Session:Skipping unknown command cmd: 0x75, payload: b'\x00\x00\x00' DEBUG:urllib3.connectionpool:https://apresolve.spotify.com:443 "GET /?type=dealer HTTP/1.1" 200 None DEBUG:Librespot:TokenProvider:Token expired or not suitable, requesting again. scopes: ['playlist-read'], old_token: None DEBUG:Librespot:MercuryClient:Send Mercury request, seq: 0, uri: hm://keymaster/token/authenticated?scope=playlist-read&client_id=xxxxxxxxxxxxxxxx&device_id=xxxxxxxxxxxxxxxx, method: GET DEBUG:Librespot:MercuryClient:Handling packet, cmd: 0xb2, seq: 0, flags: b'\x01', parts: 2 DEBUG:Librespot:TokenProvider:Updated token successfully! scopes: ['playlist-read'], new_token: <librespot.core.TokenProvider.StoredToken object at 0xb5c38f28> INFO:Librespot:Session:Authenticated as xxxxx! DEBUG:spodcast.app:episode_id None. show_id 1OLcQdw2PFDPG1jo3s0wbp INFO:spodcast.podcast:Fetching episodes... DEBUG:Librespot:TokenProvider:Token expired or not suitable, requesting again. scopes: ['user-read-email'], old_token: None DEBUG:Librespot:MercuryClient:Send Mercury request, seq: 1, uri: hm://keymaster/token/authenticated?scope=user-read-email&client_id=xxxxxxxxxxxxxxxx&device_id=xxxxxxxxxxxxxxxx, method: GET DEBUG:Librespot:MercuryClient:Handling packet, cmd: 0xb2, seq: 1, flags: b'\x01', parts: 2 DEBUG:Librespot:TokenProvider:Updated token successfully! scopes: ['user-read-email'], new_token: <librespot.core.TokenProvider.StoredToken object at 0xb5c388e0> DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=0 HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=50 HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=100 HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=150 HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=200 HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=250 HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=300 HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=350 HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/shows/1OLcQdw2PFDPG1jo3s0wbp/episodes?limit=50&offset=400 HTTP/1.1" 200 None INFO:spodcast.podcast:Fetching episode information... DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.spotify.com:443 DEBUG:urllib3.connectionpool:https://api.spotify.com:443 "GET /v1/episodes/4cSLQpgJt3mIky4fnjnsPb HTTP/1.1" 200 None DEBUG:spodcast.podcast:episode info: {'audio_preview_url': 'https://p.scdn.co/mp3-preview/298d41f47db5ba1832183ee0ee563ce30e524650', 'content_type': 'PODCAST_EPISODE', 'description': 'Wie Nacktmulle ihr schreckliches Regiment führen und E-Scooter ellipsenförmige Problemzonen schaffen. Ey Leute! Frech kommt weiter. Einfach mal müde ne Folge aufnehmen und auf dem flambierten Parmesanrad in den Sonnenuntergang reiten. Learn more about your ad choices. Visit podcastchoices.com/adchoices', 'duration_ms': 2978246, 'explicit': False, 'external_urls': {'spotify': 'https://open.spotify.com/episode/4cSLQpgJt3mIky4fnjnsPb'}, 'href': 'https://api.spotify.com/v1/episodes/4cSLQpgJt3mIky4fnjnsPb', 'html_description': '

    Wie Nacktmulle ihr schreckliches Regiment führen und E-Scooter ellipsenförmige Problemzonen schaffen. Ey Leute! Frech kommt weiter. Einfach mal müde ne Folge aufnehmen und auf dem flambierten Parmesanrad in den Sonnenuntergang reiten.

    Learn more about your ad choices. Visit podcastchoices.com/adchoices

    ', 'id': '4cSLQpgJt3mIky4fnjnsPb', 'images': [{'height': 640, 'url': 'https://i.scdn.co/image/ab6765630000ba8a21e277ef5fc2ffa449254d37', 'width': 640}, {'height': 300, 'url': 'https://i.scdn.co/image/ab67656300005f1f21e277ef5fc2ffa449254d37', 'width': 300}, {'height': 64, 'url': 'https://i.scdn.co/image/ab6765630000f68d21e277ef5fc2ffa449254d37', 'width': 64}], 'is_externally_hosted': False, 'is_paywall_content': False, 'is_playable': True, 'language': 'de', 'languages': ['de'], 'name': 'Einfach müde', 'release_date': '2022-06-18', 'release_date_precision': 'day', 'resume_point': {'fully_played': False, 'resume_position_ms': 0}, 'show': {'available_markets': ['AD', 'AE', 'AG', 'AL', 'AM', 'AO', 'AR', 'AT', 'AU', 'AZ', 'BA', 'BB', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BN', 'BO', 'BR', 'BS', 'BT', 'BW', 'BZ', 'CA', 'CH', 'CI', 'CL', 'CM', 'CO', 'CR', 'CV', 'CW', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'ES', 'FI', 'FJ', 'FM', 'FR', 'GA', 'GB', 'GD', 'GE', 'GH', 'GM', 'GN', 'GQ', 'GR', 'GT', 'GW', 'GY', 'HK', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IN', 'IS', 'IT', 'JM', 'JO', 'JP', 'KE', 'KH', 'KI', 'KM', 'KN', 'KW', 'LA', 'LB', 'LC', 'LI', 'LR', 'LS', 'LT', 'LU', 'LV', 'MA', 'MC', 'ME', 'MG', 'MH', 'MK', 'ML', 'MN', 'MO', 'MR', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NE', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NZ', 'OM', 'PA', 'PE', 'PG', 'PH', 'PL', 'PS', 'PT', 'PW', 'PY', 'QA', 'RO', 'RS', 'RW', 'SA', 'SB', 'SC', 'SE', 'SG', 'SI', 'SK', 'SL', 'SM', 'SN', 'SR', 'ST', 'SV', 'SZ', 'TD', 'TG', 'TH', 'TL', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'US', 'UY', 'UZ', 'VC', 'VN', 'VU', 'WS', 'XK', 'ZA', 'ZM', 'ZW'], 'copyrights': [], 'description': 'Fest & Flauschig mit Jan Böhmermann und Olli Schulz. Der preisgekrönte, verblüffend fabelhafte, grenzenlos fantastische Podcast für sie, ihn und es.', 'explicit': False, 'external_urls': {'spotify': 'https://open.spotify.com/show/1OLcQdw2PFDPG1jo3s0wbp'}, 'href': 'https://api.spotify.com/v1/shows/1OLcQdw2PFDPG1jo3s0wbp', 'html_description': '

    Fest & Flauschig mit Jan Böhmermann und Olli Schulz. Der preisgekrönte, verblüffend fabelhafte, grenzenlos fantastische Podcast für sie, ihn und es.

    ', 'id': '1OLcQdw2PFDPG1jo3s0wbp', 'images': [{'height': 640, 'url': 'https://i.scdn.co/image/ab6765630000ba8a21e277ef5fc2ffa449254d37', 'width': 640}, {'height': 300, 'url': 'https://i.scdn.co/image/ab67656300005f1f21e277ef5fc2ffa449254d37', 'width': 300}, {'height': 64, 'url': 'https://i.scdn.co/image/ab6765630000f68d21e277ef5fc2ffa449254d37', 'width': 64}], 'is_externally_hosted': False, 'languages': ['de'], 'media_type': 'audio', 'name': 'Fest & Flauschig', 'publisher': 'Jan Böhmermann & Olli Schulz', 'total_episodes': 415, 'type': 'show', 'uri': 'spotify:show:1OLcQdw2PFDPG1jo3s0wbp'}, 'type': 'episode', 'uri': 'spotify:episode:4cSLQpgJt3mIky4fnjnsPb'} DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api-partner.spotify.com:443 DEBUG:urllib3.connectionpool:https://api-partner.spotify.com:443 "GET /pathfinder/v1/query?operationName=getEpisode&variables=%7B%22uri%22:%22spotify:episode:4cSLQpgJt3mIky4fnjnsPb%22%7D&extensions=%7B%22persistedQuery%22:%7B%22version%22:1,%22sha256Hash%22:%22224ba0fd89fcfdfb3a15fa2d82a6112d3f4e2ac88fba5c6713de04d1b72cf482%22%7D%7D HTTP/1.1" 200 None DEBUG:spodcast.podcast:('{"data":{"episode":{"id":"4cSLQpgJt3mIky4fnjnsPb","uri":"spotify:episode:4cSLQpgJt3mIky4fnjnsPb","name":"Einfach müde","htmlDescription":"

    Wie Nacktmulle ihr schreckliches Regiment führen und E-Scooter ellipsenförmige Problemzonen schaffen. Ey Leute! Frech kommt weiter. Einfach mal müde ne Folge aufnehmen und auf dem flambierten Parmesanrad in den Sonnenuntergang reiten.

    Learn more about your ad choices. Visit <a href=\"https://podcastchoices.com/adchoices\" rel=\"nofollow\">podcastchoices.com/adchoices

    ","description":"Wie Nacktmulle ihr schreckliches Regiment führen und E-Scooter ellipsenförmige Problemzonen schaffen. Ey Leute! Frech kommt weiter. Einfach mal müde ne Folge aufnehmen und auf dem flambierten Parmesanrad in den Sonnenuntergang reiten. Learn more about your ad choices. Visit podcastchoices.com/adchoices","duration":{"totalMilliseconds":2978246},"audio":{"items":[{"url":"https://p.scdn.co/mp3-preview/649ad573183f3e299658d43dcead5fd5e71e2c78","format":"AAC_24","fileId":"649ad573183f3e299658d43dcead5fd5e71e2c78","externallyHosted":false},{"url":"https://p.scdn.co/mp3-preview/531e8201de62066a4f78427f378d134e3e87701b","format":"MP4_128_DUAL","fileId":"531e8201de62066a4f78427f378d134e3e87701b","externallyHosted":false},{"url":"https://p.scdn.co/mp3-preview/e1f819cf9b3c282b00a64e26f12718c64a28fd60","format":"MP4_128","fileId":"e1f819cf9b3c282b00a64e26f12718c64a28fd60","externallyHosted":false},{"url":"https://p.scdn.co/mp3-preview/8f4418c60184e9b0776ae16e4d2766024d103f8d","format":"OGG_VORBIS_96","fileId":"8f4418c60184e9b0776ae16e4d2766024d103f8d","externallyHosted":false}]},"audioPreview":{"url":"https://p.scdn.co/mp3-preview/298d41f47db5ba1832183ee0ee563ce30e524650","format":"MP3_96"},"playability":{"playable":true,"reason":"PLAYABLE"},"playedState":{"playPositionMilliseconds":0,"state":"NOT_STARTED"},"releaseDate":{"isoString":"2022-06-18T21:50:00Z"},"contentRating":{"label":"NONE"},"coverArt":{"sources":[{"url":"https://i.scdn.co/image/ab6765630000f68d21e277ef5fc2ffa449254d37","width":64,"height":64},{"url":"https://i.scdn.co/image/ab67656300005f1f21e277ef5fc2ffa449254d37","width":300,"height":300},{"url":"https://i.scdn.co/image/ab6765630000ba8a21e277ef5fc2ffa449254d37","width":640,"height":640}]},"type":"PODCAST_EPISODE","podcast":{"uri":"spotify:show:1OLcQdw2PFDPG1jo3s0wbp","name":"Fest & Flauschig","coverArt":{"sources":[{"url":"https://i.scdn.co/image/ab6765630000f68d21e277ef5fc2ffa449254d37","width":64,"height":64},{"url":"https://i.scdn.co/image/ab67656300005f1f21e277ef5fc2ffa449254d37","width":300,"height":300},{"url":"https://i.scdn.co/image/ab6765630000ba8a21e277ef5fc2ffa449254d37","width":640,"height":640}]},"trailer":null,"showTypes":["SHOW_TYPE_EXCLUSIVE"]},"sharingInfo":{"shareUrl":"https://open.spotify.com/episode/4cSLQpgJt3mIky4fnjnsPb?si=FUC2Gy4tQFOJ2CUmXOxVyA","shareId":"FUC2Gy4tQFOJ2CUmXOxVyA"},"segments":{"segments":{"totalCount":0}}}},"extensions":{"cacheControl":{"version":1.0,"hints":[]}}}', {'data': {'episode': {'id': '4cSLQpgJt3mIky4fnjnsPb', 'uri': 'spotify:episode:4cSLQpgJt3mIky4fnjnsPb', 'name': 'Einfach müde', 'htmlDescription': '

    Wie Nacktmulle ihr schreckliches Regiment führen und E-Scooter ellipsenförmige Problemzonen schaffen. Ey Leute! Frech kommt weiter. Einfach mal müde ne Folge aufnehmen und auf dem flambierten Parmesanrad in den Sonnenuntergang reiten.

    Learn more about your ad choices. Visit podcastchoices.com/adchoices

    ', 'description': 'Wie Nacktmulle ihr schreckliches Regiment führen und E-Scooter ellipsenförmige Problemzonen schaffen. Ey Leute! Frech kommt weiter. Einfach mal müde ne Folge aufnehmen und auf dem flambierten Parmesanrad in den Sonnenuntergang reiten. Learn more about your ad choices. Visit podcastchoices.com/adchoices', 'duration': {'totalMilliseconds': 2978246}, 'audio': {'items': [{'url': 'https://p.scdn.co/mp3-preview/649ad573183f3e299658d43dcead5fd5e71e2c78', 'format': 'AAC_24', 'fileId': '649ad573183f3e299658d43dcead5fd5e71e2c78', 'externallyHosted': False}, {'url': 'https://p.scdn.co/mp3-preview/531e8201de62066a4f78427f378d134e3e87701b', 'format': 'MP4_128_DUAL', 'fileId': '531e8201de62066a4f78427f378d134e3e87701b', 'externallyHosted': False}, {'url': 'https://p.scdn.co/mp3-preview/e1f819cf9b3c282b00a64e26f12718c64a28fd60', 'format': 'MP4_128', 'fileId': 'e1f819cf9b3c282b00a64e26f12718c64a28fd60', 'externallyHosted': False}, {'url': 'https://p.scdn.co/mp3-preview/8f4418c60184e9b0776ae16e4d2766024d103f8d', 'format': 'OGG_VORBIS_96', 'fileId': '8f4418c60184e9b0776ae16e4d2766024d103f8d', 'externallyHosted': False}]}, 'audioPreview': {'url': 'https://p.scdn.co/mp3-preview/298d41f47db5ba1832183ee0ee563ce30e524650', 'format': 'MP3_96'}, 'playability': {'playable': True, 'reason': 'PLAYABLE'}, 'playedState': {'playPositionMilliseconds': 0, 'state': 'NOT_STARTED'}, 'releaseDate': {'isoString': '2022-06-18T21:50:00Z'}, 'contentRating': {'label': 'NONE'}, 'coverArt': {'sources': [{'url': 'https://i.scdn.co/image/ab6765630000f68d21e277ef5fc2ffa449254d37', 'width': 64, 'height': 64}, {'url': 'https://i.scdn.co/image/ab67656300005f1f21e277ef5fc2ffa449254d37', 'width': 300, 'height': 300}, {'url': 'https://i.scdn.co/image/ab6765630000ba8a21e277ef5fc2ffa449254d37', 'width': 640, 'height': 640}]}, 'type': 'PODCAST_EPISODE', 'podcast': {'uri': 'spotify:show:1OLcQdw2PFDPG1jo3s0wbp', 'name': 'Fest & Flauschig', 'coverArt': {'sources': [{'url': 'https://i.scdn.co/image/ab6765630000f68d21e277ef5fc2ffa449254d37', 'width': 64, 'height': 64}, {'url': 'https://i.scdn.co/image/ab67656300005f1f21e277ef5fc2ffa449254d37', 'width': 300, 'height': 300}, {'url': 'https://i.scdn.co/image/ab6765630000ba8a21e277ef5fc2ffa449254d37', 'width': 640, 'height': 640}]}, 'trailer': None, 'showTypes': ['SHOW_TYPE_EXCLUSIVE']}, 'sharingInfo': {'shareUrl': 'https://open.spotify.com/episode/4cSLQpgJt3mIky4fnjnsPb?si=FUC2Gy4tQFOJ2CUmXOxVyA', 'shareId': 'FUC2Gy4tQFOJ2CUmXOxVyA'}, 'segments': {'segments': {'totalCount': 0}}}}, 'extensions': {'cacheControl': {'version': 1.0, 'hints': []}}}) DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api-partner.spotify.com:443 DEBUG:urllib3.connectionpool:https://api-partner.spotify.com:443 "GET /pathfinder/v1/query?operationName=getEpisode&variables=%7B%22uri%22:%22spotify:episode:4cSLQpgJt3mIky4fnjnsPb%22%7D&extensions=%7B%22persistedQuery%22:%7B%22version%22:1,%22sha256Hash%22:%22224ba0fd89fcfdfb3a15fa2d82a6112d3f4e2ac88fba5c6713de04d1b72cf482%22%7D%7D HTTP/1.1" 200 None DEBUG:spodcast.podcast:download_url: https://p.scdn.co/mp3-preview/8f4418c60184e9b0776ae16e4d2766024d103f8d DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): p.scdn.co:443 DEBUG:urllib3.connectionpool:https://p.scdn.co:443 "GET /mp3-preview/8f4418c60184e9b0776ae16e4d2766024d103f8d HTTP/1.1" 404 0 Traceback (most recent call last): File "/usr/local/bin/spodcast", line 8, in sys.exit(main()) File "/usr/local/lib/python3.9/dist-packages/spodcast/main.py", line 42, in main args.func(args) File "/usr/local/lib/python3.9/dist-packages/spodcast/app.py", line 24, in client download_episode(episode) File "/usr/local/lib/python3.9/dist-packages/spodcast/podcast.py", line 172, in download_episode path, size, mimetype = download_file(download_url, filepath) File "/usr/local/lib/python3.9/dist-packages/spodcast/podcast.py", line 83, in download_file r.raise_for_status() # Will only raise for 4xx codes, so... File "/usr/local/lib/python3.9/dist-packages/requests/models.py", line 960, in raise_for_status raise HTTPError(http_error_msg, response=self) requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://p.scdn.co/mp3-preview/8f4418c60184e9b0776ae16e4d2766024d103f8d

    Notes:

    • It happens for multiple podcasts I've tried, e.g.: https://open.spotify.com/show/6UUIXmp1V0fK4ZpK7vzAbQ https://open.spotify.com/show/1OLcQdw2PFDPG1jo3s0wbp
    • I intentionally blacked out device id, client id and my username

    Let me know if I can assist somehow:) Thanks!

    bug 
    opened by diveflo 18
  • added Dockerfile

    added Dockerfile

    Assumes that you will build the image from the root directory of the repository

    i.e. (using my fork/branch as an example)

    git clone -b dockerfile https://github.com/heywoodlh/Spodcast 
    cd Spodcast &&\
        docker build -t heywoodlh/spodcast -f docker/Dockerfile . 
    

    Then you can run the container like this:

    docker run -it --rm -v /tmp/spodcast:/spodcast heywoodlh/spodcast -c /spodcast/html/spodcast.json --root-path /spodcast/html --credentials-location /spodcast/creds.json -p -l /spodcast/spotify.rc
    

    Related, I've also created a Github Action to build a multi-architecture image and push it to Docker Hub: https://github.com/heywoodlh/actions/blob/master/.github/workflows/spodcast-buildx.yml

    If you merge this pull request I will update my Action to point to your repo and continue building images at heywoodlh/spodcast on Docker Hub and I'm happy to keep building them on a weekly basis on my account -- but it'd be awesome if you had an official, cross-architecture image for Spodcast. Feel free to just take my Github Action and implement it here (or I can create a PR for that as well).

    Let me know if that makes sense or if I can be of any help. Thanks for a great project!

    opened by heywoodlh 17
  • Set audio format (.ogg vs. .mp3)

    Set audio format (.ogg vs. .mp3)

    Some podcasts are downloaded as .mp3 and some as .ogg. I looked at your code and it seems to me, that you are preferring .ogg over .mp3.

    In which format are the files stored on Spotify's servers? Either .mp3 or .ogg? Or both formats for some podcasts? Would it be possible to download the .mp3 for the episodes which are currently downloaded as .ogg?

    opened by ahuse 13
  • Podcast episodes are not completely downloading

    Podcast episodes are not completely downloading

    I noticed that for some podcasts the end of the episodes are missing.

    For example I downloaded this show: https://open.spotify.com/show/5JYitG4bOM3sVmAQRdX1Na and for every episode the last minute or so is missing in the downloaded file.

    After running the debug output I noticed that Librespot stopped downloading before all chunks were completed:

    DEBUG:Librespot:Session:Chunk 156/181 completed, cached: False, stream: file_id: 7c9d5d4e37e4fb6fd91b2460c54afb2059c11daa
    
    DEBUG:urllib3.connectionpool:https://audio4-ak-spotify-com.akamaized.net:443 "GET /audio/7c9d5d4e37e4fb6fd91b2460c54afb2059c11daa?__token__=exp=1657893805~hmac=9f418cd6ab2eaa013c4147924038e9eda979bb052984e084697417f1b16659e9 HTTP/1.1" 206 131072
    
    DEBUG:Librespot:Session:Chunk 157/181 completed, cached: False, stream: file_id: 7c9d5d4e37e4fb6fd91b2460c54afb2059c11daa
    
    DEBUG:urllib3.connectionpool:https://audio4-ak-spotify-com.akamaized.net:443 "GET /audio/7c9d5d4e37e4fb6fd91b2460c54afb2059c11daa?__token__=exp=1657893805~hmac=9f418cd6ab2eaa013c4147924038e9eda979bb052984e084697417f1b16659e9 HTTP/1.1" 206 131072
    
    DEBUG:Librespot:Session:Chunk 158/181 completed, cached: False, stream: file_id: 7c9d5d4e37e4fb6fd91b2460c54afb2059c11daa
    
    INFO:spodcast.podcast:transcoding ogg->mp3
    

    This has to be some issues with certain shows, as either all of the episodes of a show are missing the last minute or they are all fine.

    An example for a working podcast would be: https://open.spotify.com/show/7BTOsF2boKmlYr76BelijW

    I have no clue what the issue could be and am open for any suggestion!

    bug 
    opened by lohningerthomas 12
  • Logs for the Feed update

    Logs for the Feed update

    Is there any logs for the feed update that is set inside the web application? My feeds are not fetching new episodes automatically even after many days the new episode is available in Spotify. When I manually use the "refresh" feature the the episode is fetched. With some logs I'll to check if the schedule is acctually running or any errors are occuring.

    opened by lucashmsilva 11
  • PHP URL Problems

    PHP URL Problems

    So I'm running Spodcast on my own server at home -- it is not accessible to the world.

    I use FreshRSS for aggregating feeds. For some reason (I'm sure a misconfiguration on my end) the URLs for the media are all messed up when I try the default configuration and subscribing via FreshRSS.

    This is what one of the media URLs looks like when I subscribe via FreshRSS (which is a web app running at http://freshrss.local): http://freshrss.local/i/spodcast.local/The_Joe_Rogan_Experience/The_Joe_Rogan_Experience_-_1782_-_Daniel_Holzman.ogg

    What it actually should be is this (notice that the freshrss.local piece is missing): http://spodcast.local/The_Joe_Rogan_Experience/The_Joe_Rogan_Experience_-_1782_-_Daniel_Holzman.ogg

    So for some reason it is prepending the URL of my FreshRSS web app to the media URL when I attempt to subscribe to the RSS feed.

    I was able to workaround this by modifying The_Joe_Rogan_Experience's .index.php file and hardcoding the URL I use into the generated media URL:

    $hardcoded_url = "http://spodcast.local{$_SERVER['REQUEST_URI']}";
    ...
    ...
    echo "            <media:content url=\"".$hardcoded_url.$info->filename."\" medium=\"".$info->medium."\" duration=\"".$info->duration."\" type=\"".$info->mimetype."\" />\n";
    ...
    ...
    
    

    I'm not proficient with PHP at all so is there a better way to solve this problem?

    bug 
    opened by heywoodlh 6
  • Increase maximum episodes number in feed manager

    Increase maximum episodes number in feed manager

    Hi,

    would it be possible to allow a higher number than 5 episodes to sync (max episodes) in the feed manager? I have a podcast that somehow have their episodes in the wrong order so the automatic sync isn't working for this one.

    Thanks!

    enhancement question 
    opened by diveflo 4
  • duration_ms is not defined

    duration_ms is not defined

    Traceback (most recent call last):
      File "/usr/local/bin/spodcast", line 8, in <module>
        sys.exit(main())
      File "/usr/local/lib/python3.7/site-packages/spodcast/__main__.py", line 39, in main
        args.func(args)
      File "/usr/local/lib/python3.7/site-packages/spodcast/app.py", line 24, in client
        download_episode(episode)
      File "/usr/local/lib/python3.7/site-packages/spodcast/podcast.py", line 143, in download_episode
        path, size = download_stream(stream, filepath)
      File "/usr/local/lib/python3.7/site-packages/spodcast/podcast.py", line 115, in download_stream
        delta_want = (downloaded / size) * (duration_ms/1000)
    NameError: name 'duration_ms' is not defined
    

    This error is not very verbose, how do I work past it?

    Here's my command:

    spodcast --root-path /tmp/spodcast/html --credentials-location /tmp/spodcast/creds.json --max-episodes 10 -p --chunk-size 1000 --log-level debug --rss urls 'https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk'
    
    opened by heywoodlh 2
  • Can't add feeds

    Can't add feeds

    I've set up Spodcast following your tutorial and I think I've set all permissions correctly. www-data can run spodcast.

    When adding a url via the web interface, the spinner just disappears after a while and the page stays empty.

    I've set "LOG_LEVEL":"DEBUG" and ran the following command as user www-data:

    spodcast -c /mnt/audio/spodcast/spodcast.json --max-episodes 1 https://open.spotify.com/show/1OLcQdw2PFDPG1jo3s0wbp

    Spodcast seems to find the podcast episode but skips it for some reason. Any help or direction is greatly appreciated! This is the console output:

    [email protected]:/mnt/audio/spodcast$ spodcast -c /mnt/audio/spodcast/spodcast.json --max-episodes 1 https://open.spotify.com/show/1OLcQdw2PFDPG1jo3s0wbp DEBUG:spodcast.spodcast:args: Namespace(config_location='/mnt/audio/spodcast/spodcast.json', prepare_feed=False, urls=['https://open.spotify.com/show/1OLcQdw2PFDPG1jo3s0wbp'], login=None, root_path=None, skip_existing=None, retry=None, max_episodes='1', chunk_size=None, download_real_time=None, language=None, credentials_location=None, rss_feed=None, transcode=None, log_level=None, func=<function client at 0x7fce4ac41c10>) DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): apresolve.spotify.com:443 DEBUG:urllib3.connectionpool:https://apresolve.spotify.com:443 "GET /?type=accesspoint HTTP/1.1" 200 None INFO:Librespot:Session:Created new session! device_id: d6f07bbe25096c88a475cea32cec6fb71ec3376c, ap: ap-gae2.spotify.com:443 INFO:Librespot:Session:Connection successfully! INFO:Librespot:Session:Session.Receiver started DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): apresolve.spotify.com:443 DEBUG:urllib3.connectionpool:https://apresolve.spotify.com:443 "GET /?type=spclient HTTP/1.1" 200 None DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): apresolve.spotify.com:443 INFO:Librespot:Session:Skipping 02 INFO:Librespot:Session:Received license_version: 0 INFO:Librespot:Session:Received country_code: DE DEBUG:Librespot:Session:Parsed product info: {'type': 'open', 'ab-ad-player-targeting': '1', 'ab-ad-requester': '1', 'ab-android-push-notifications': '1', 'ab-browse-music-tuesday': '1', 'ab-collection-bookmark-model': '1', 'ab-collection-followed-artists-only': '1', 'ab-collection-hide-unavailable-albums': '0', 'ab-collection-offline-mode': '0', 'ab-collection-union': '1', 'ab-desktop-hide-follow': '0', 'ab-desktop-playlist-annotation-edit': '1', 'ab-mobile-discover': '0', 'ab-mobile-running-onlymanualmode': 'only-manual', 'ab-mobile-running-tempo-detection': 'Control', 'ab-mobile-social-feed': '1', 'ab-mobile-startpage': '0', 'ab-moments-experience': '0', 'ab-new-share-flow': '0', 'ab-play-history': '0', 'ab-playlist-extender': '5', 'ab-sugarpills-sanity-check': '0', 'ab-test-group': '779', 'ab-watch-now': '0', 'ab_recently_played_feature_time_filter_threshold': 'com.spotify.gaia=30,driving-mode=120,spotify%3Ainternal%3Astartpage=30', 'ad-catalogues': 'spotify', 'ad-formats-preroll-video': '0', 'ad-formats-video-takeover': '1', 'ad-persist-reward-time': '0', 'ad-session-persistence': '1', 'ad-use-adlogic': 'stream', 'ads': '1', 'allow-override-internal-prefs': '0', 'ap-resolve-pods': '0', 'app-developer': '0', 'arsenal_country': '1', 'audio-preview-url-template': 'https://p.scdn.co/mp3-preview/{id}', 'backend-advised-bitrate': '1', 'browse-overview-enabled': '1', 'buffering-strategy': '0', 'buffering-strategy-parameters': '0.8:0.2:0.0:0.0:0.0:0.0:1.0:10:10:2000:10000:10485760', 'capper-profile': None, 'capping-bar-threshold': '3601', 'catalogue': 'free', 'collection': '1', 'created_by_partner': None, 'enable-annotations': '2', 'enable-annotations-read': '0', 'enable-autostart': '1', 'enable-crossfade': '1', 'enable-gapless': '1', 'expiry': '1', 'explicit-content': '1', 'fb-grant-permission-local-render': '0', 'fb-info-confirmation': 'control', 'financial-product': 'pr:open,tc:0', 'head-file-caching': '1', 'head-files': '1', 'head-files-url': 'https://heads-fa.scdn.co/head/{file_id}', 'high-bitrate': '0', 'image-url': 'https://i.scdn.co/image/{file_id}', 'incognito_mode_timeout': '21600', 'india-experience': '0', 'instant-search': '0', 'instant-search-expand-sidebar': '0', 'is_email_verified': '1', 'key-caching-max-count': '10000', 'key-caching-max-offline-seconds': '1800', 'key-memory-cache-mode': '1:15,300', 'lastfm-session': None, 'libspotify': '0', 'license-acceptance-grace-days': '30', 'license-agreements': None, 'local-files-import': '0', 'metadata-link-lookup-modes': '0', 'mobile': '0', 'mobile-browse': '0', 'mobile-login': '0', 'mobile-payment': '0', 'name': 'Spotify Free', 'network-operator-premium-activation': '1', 'nft-disabled': '1', 'npt-disabled': '2', 'offline': '0', 'on-demand': '1', 'pause-after': '18000', 'payments-locked-state': '0', 'player-license': 'on-demand', 'playlist-annotations-markup': '0', 'playlist-folders': '1', 'preferred-locale': 'en', 'prefetch-keys': '1', 'prefetch-strategy': '0', 'prefetch-window-max': '2', 'profile-image-upload': '1', 'publish-activity': '0', 'publish-playlist': '1', 'radio': '1', 'remote-control': '6', 'send-email': '0', 'shows-collection': '1', 'shows-collection-jam': '1', 'shuffle': '0', 'shuffle-algorithm': '1', 'sidebar-navigation-enabled': '0', 'storage-size-config': '10240,90,500,3', 'streaming': '1', 'streaming-rules': None, 'track-cap': '0', 'ugc-abuse-report': '1', 'ugc-abuse-report-url': 'https://support.spotify.com/abuse/?uri={uri}', 'use-fb-publish-backend': '2', 'use-pl3': '0', 'use-playlist-app': '0', 'use-playlist-uris': '0', 'user-profile-show-invitation-codes': '0', 'video-cdn-sampling': '1', 'video-device-blacklisted': '0', 'video-initial-bitrate': '200000', 'video-keyframe-url': 'http://keyframes-fa.cdn.spotify.com/keyframes/v1/sources/{source_id}/keyframe/heights/{height}/timestamps/{timestamp_ms}.jpg', 'video-manifest-url': 'https://spclient.wg.spotify.com/manifests/v6/{type}/sources/{source_id}/options/supports_drm', 'video-wifi-initial-bitrate': '800000', 'wanted-licenses': None, 'widevine-license-url': 'https://spclient.wg.spotify.com/widevine-license/v1/video/license'} INFO:Librespot:Session:Skipping 1f INFO:Librespot:Session:Skipping 69 DEBUG:urllib3.connectionpool:https://apresolve.spotify.com:443 "GET /?type=dealer HTTP/1.1" 200 None DEBUG:Librespot:TokenProvider:Token expired or not suitable, requesting again. scopes: ['playlist-read'], old_token: None DEBUG:Librespot:MercuryClient:Send Mercury request, seq: 0, uri: hm://keymaster/token/authenticated?scope=playlist-read&client_id=65b708073fc0480ea92a077233ca87bd&device_id=d6f07bbe25096c88a475cea32cec6fb71ec3376c, method: GET DEBUG:Librespot:MercuryClient:Handling packet, cmd: 0xb5, seq: 8560867902608113664, flags: b'\x01', parts: 1 DEBUG:Librespot:MercuryClient:Couldn't dispatch Mercury event seq: 8560867902608113664, uri: hm://pusher/v1/connections/ZDZmMDdiYmUyNTA5NmM4OGE0NzVjZWEzMmNlYzZmYjcxZWMzMzc2YytBUCt0Y3A6Ly9nYWUyLWFjY2Vzc3BvaW50LWEtNnZueC5nYWUyLnNwb3RpZnkubmV0OjUwMDkrNTVEMjlGMUI5QjkwOTBCN0YxOTNBMTREMTBDQjYxMTlCMjRDRUNCOThERDIyQTVEN0M4MUEzMkE4MUI5QjJCOA%3D%3D, code: 200, payload: b'' DEBUG:Librespot:Session:Received 0x10: 28c3d872056c29887aa89b2bb07525c155531adf DEBUG:Librespot:MercuryClient:Handling packet, cmd: 0xb2, seq: 0, flags: b'\x01', parts: 2 DEBUG:Librespot:TokenProvider:Updated token successfully! scopes: ['playlist-read'], new_token: <librespot.core.TokenProvider.StoredToken object at 0x7fce46ea1a00> INFO:Librespot:Session:Authenticated as 315q64kqplnebfldqygmmqpqelye! DEBUG:spodcast.app:episode_id_str None. show_id_str 1OLcQdw2PFDPG1jo3s0wbp DEBUG:spodcast.app:show_id: <librespot.metadata.ShowId object at 0x7fce46e635b0> INFO:Librespot:Session:Skipping unknown command cmd: 0x75, payload: b'\x00\x00\x00' DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): clienttoken.spotify.com:443 DEBUG:urllib3.connectionpool:https://clienttoken.spotify.com:443 "POST /v1/clienttoken HTTP/1.1" 200 None DEBUG:Librespot:ApiClient:Updated client token: AABawkvhKpjRCTeXYN8J9Fm7whJBiyYqFevKcp/uBKjE8xEJD56Wh9uH5hjEQw3+p8Qw1wE395/TCXem8+Puv960Ckf8VQ3b/Y1ziwWY6pVBa+3Pqx3/7eHIGcGd6BBhDhKzWuT2bWBdxCbBXWMglyh6BuxeQO84T+B7foTQ3NTUTMLpdmz7GyflIfgRjOIa/HwXQWLIB1HxQ3nreDq5xunuKXj9kUCwy7r5Oc5j5dFPDCeV DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): guc3-spclient.spotify.com:443 DEBUG:urllib3.connectionpool:https://guc3-spclient.spotify.com:443 "GET /metadata/4/show/3bc200e4655841529d8a7a3d2d027e23 HTTP/1.1" 200 15822 DEBUG:spodcast.app:episode_id: e1a3290cae685f4ca143a3afb8fed29e INFO:spodcast.podcast:Fetching episode information... DEBUG:urllib3.connectionpool:https://guc3-spclient.spotify.com:443 "GET /metadata/4/episode/e1a3290cae685f4ca143a3afb8fed29e HTTP/1.1" 200 796 DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): traffic.megaphone.fm:443 DEBUG:urllib3.connectionpool:https://traffic.megaphone.fm:443 "GET /GLT9899421262.mp3?updated=1661274798 HTTP/1.1" 302 102 DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): dcs.megaphone.fm:443 DEBUG:urllib3.connectionpool:https://dcs.megaphone.fm:443 "GET /GLT9899421262.mp3?key=20c38c11dee1691c0afed7c44f8d5525 HTTP/1.1" 200 69670934 INFO:spodcast.podcast:Skipped Fest & Flauschig: #BOOMERCRINGE 34 INFO:spodcast.podcast:Fetching show information... DEBUG:urllib3.connectionpool:https://guc3-spclient.spotify.com:443 "GET /metadata/4/show/3bc200e4655841529d8a7a3d2d027e23 HTTP/1.1" 200 15822

    opened by lkstnr 1
  • spodcast-the-command clobbers sync/keep info, working on it...

    spodcast-the-command clobbers sync/keep info, working on it...

    FYI, I noticed that spodcast (the Python command) clobbers sync- and keep--related data in the show info file which leads to these values being reset to their defaults when spodcast is invoked. This will be solved in the upcoming release.

    opened by Yetangitu 1
  • StatusCodeEception: 451

    StatusCodeEception: 451

    Hi all, downloading the following podcast https://open.spotify.com/show/6gAIR3HN25mgU1niAZyCm0?si=b2dceeeb02a34a2a results in WARNING:spodcast.podcast:episode 6a1e2afa822c5a34bb1b80bfdd7d0b6d, StatusCodeException: 451

    Example code: spodcast --skip-existing yes --rss-feed no --transcode no --max-episodes 1 https://open.spotify.com/show/6gAIR3HN25mgU1niAZyCm0?si=b2dceeeb02a34a2a

    Downloading individual episodes seems to work. Anyone an idea why? Thanks for your help!

    opened by BumbleCast 1
  • Transcoding not working with docker-compose setup

    Transcoding not working with docker-compose setup

    I can't for the life of me get transcoding to work when using the docker-compose.yml setup. I can see the command is parsed, but no transcoding occurs.

    opened by jamesvincent 0
  • Symbols in password causes errors using docker-compose

    Symbols in password causes errors using docker-compose

    spodcast-spodcast-cron-1  | crond: USER root pid  34 cmd SPODCAST_ROOT="/data" SPODCAST_HTML="/data/html" SPODCAST_CONFIG_JSON="/data/spodcast.json" SPOTIFY_CREDS_JSON="/data/creds.json" SPOTIFY_RC_PATH="/data/spotify.rc" **SPOTIFY_PASSWORD="pnI7**
    spodcast-spodcast-cron-1  | crond: child running /bin/bash
    spodcast-spodcast-cron-1  | crond: USER root pid  35 cmd chown -R 101:101 /data/html
    spodcast-spodcast-cron-1  | /bin/bash: -c: line 1: unexpected EOF while looking for matching `"'
    spodcast-spodcast-cron-1  | /bin/bash: -c: line 2: syntax error: unexpected end of file
    spodcast-spodcast-cron-1  | crond: wakeup dt=10
    

    the next char in my password 'pnl7' is a symbol, #! to be precise. This caused issues when running with docker-compose.yml. Having changed my password to a more simple password, all is well using the provided compose.

    opened by jamesvincent 0
  • Errors in web interface

    Errors in web interface

    I used the docker-compose file info from this fork: https://github.com/heywoodlh/Spodcast

    When i load the webinterface i get the following errors and cannot seem to do anything inside of the webinterface

    Warning: uasort() expects parameter 1 to be array, null given in /data/html/.index.php on line 69

    Warning: array_keys() expects parameter 1 to be array, null given in /data/html/.index.php on line 505

    Warning: Invalid argument supplied for foreach() in /data/html/.index.php on line 505

    Warning: Invalid argument supplied for foreach() in /data/html/.index.php on line 581

    opened by hermy65 2
  • Show with ID starting with 0 does not (completely) work

    Show with ID starting with 0 does not (completely) work

    Hi,

    I have a podcast (https://open.spotify.com/show/09rIWfzXpDbWQf2paN0g6u) where the ID starts with a 0. I can add this via the command line and the web UI (and the download works) but its ID is actually saved without that 0 and any updates therefore don't work.

    Modifying the ID in the index.info and feeds.info files fixes the issue.

    Thanks for your help!

    opened by diveflo 0
Releases(v0.5.2)
  • v0.5.2(Jul 20, 2022)

    This release:

    • fixes #14 (some shows are not downloaded completely)
    • fixes #13 (Cannot download episodes anymore)
    • fixes the problem noted in https://github.com/Yetangitu/Spodcast/issues/13#issuecomment-1171732387 with downloading single episodes
    • uses librespot-python interfaces instead of raw web API access (needed to fix https://github.com/Yetangitu/Spodcast/issues/13)
    • can not yet determine decrypted file size for Spotify-hosted episodes (which used to work) so will only look at the file name to determine whether an episode has already been downloaded. To retry corrupted downloads just remove the partially downloaded file and try again.

    To get spodcast to fetch those episodes it missed while #13 reared its ugly head (i.e. between 2022-06-16 and 2022-06-30) you can temporarily increase --max-episodes (or sync in the web interface, although that only goes up to 5) to whatever number of episodes you expect have been missed - the number depends on the release frequency.

    Source code(tar.gz)
    Source code(zip)
    spodcast-0.5.2.tar.gz(43.19 KB)
  • v0.5.1(Jun 30, 2022)

    This release:

    • fixes #13 (Cannot download episodes anymore)
    • fixes the problem noted in https://github.com/Yetangitu/Spodcast/issues/13#issuecomment-1171732387 with downloading single episodes
    • uses librespot-python interfaces instead of raw web API access (needed to fix https://github.com/Yetangitu/Spodcast/issues/13)
    • can not yet determine decrypted file size for Spotify-hosted episodes (which used to work) so will only look at the file name to determine whether an episode has already been downloaded. To retry corrupted downloads just remove the partially downloaded file and try again.

    To get spodcast to fetch those episodes it missed while #13 reared its ugly head (i.e. between 2022-06-16 and 2022-06-30) you can temporarily increase --max-episodes (or sync in the web interface, although that only goes up to 5) to whatever number of episodes you expect have been missed - the number depends on the release frequency.

    Source code(tar.gz)
    Source code(zip)
    spodcast-0.5.1.tar.gz(43.19 KB)
  • v0.5.0(Jun 30, 2022)

    This release:

    • fixes #13 (Cannot download episodes anymore)
    • uses librespot-python interfaces instead of raw web API access (needed to fix https://github.com/Yetangitu/Spodcast/issues/13)
    • can not yet determine decrypted file size for Spotify-hosted episodes (which used to work) so will only look at the file name to determine whether an episode has already been downloaded. To retry corrupted downloads just remove the partially downloaded file and try again.

    To get spodcast to fetch those episodes it missed while #13 reared its ugly head (i.e. between 2022-06-16 and 2022-06-30) you can temporarily increase --max-episodes (or sync in the web interface, although that only goes up to 5) to whatever number of episodes you expect have been missed - the number depends on the release frequency.

    Source code(tar.gz)
    Source code(zip)
    spodcast-0.5.0.tar.gz(43.19 KB)
  • v0.4.9(May 24, 2022)

    Changes:

    • some shows publish episodes in the wrong order so sort the list before using it (fixes #11)
    • added --transcode yes/no to enable transcoding .ogg into .mp3 for handicapped devices which do not support open codes (iOS, looking at you here)
    • added webcron endpoint to run feed updates in situations where the system scheduler can not be used
    • feed manager is now mostly a single page app with live updates
    • added -v (version) option
    • added versioned updatees for feed and index manager
    • show logos are now hosted locally

    Fixes:

    • direct login now works as intended
    • keep spodcast from clobbering feed manager related data (sync/keep)
    • remove spurious quotes from transcoder configuration endpoint
    • dump amended spodcast config file in case the existing file misses one or more settings
    • fix the case of the missing $SPODCAST_COMMAND in the feed manager

    New install requirements:

    • ffmpeg-python
    • setuptools

    The change to a SPA was necessitated by the introduction of the --transcode yes/no option which (when activated) causes feed updates to take much more time, especially on less powerful hardware. This would cause the feed manager process to timeout before the feeds were updated. This problem is mostly fixed but can still occur in the webcron update process. If this happens the php-fpm and/or web server timeout needs to be increased. This should only happen on slower hardware and/or slow links. See the README for information on how to do this for nginx.

    Source code(tar.gz)
    Source code(zip)
    spodcast-0.4.9.tar.gz(43.35 KB)
  • v0.4.8(Mar 2, 2022)

    Changes:

    • added --transcode yes/no to enable transcoding .ogg into .mp3 for handicapped devices which do not support open codes (iOS, looking at you here)
    • added webcron endpoint to run feed updates in situations where the system scheduler can not be used
    • feed manager is now mostly a single page app with live updates
    • added -v (version) option
    • added versioned updatees for feed and index manager
    • show logos are now hosted locally

    Fixes:

    • direct login now works as intended
    • keep spodcast from clobbering feed manager related data (sync/keep)
    • remove spurious quotes from transcoder configuration endpoint
    • dump amended spodcast config file in case the existing file misses one or more settings
    • fix the case of the missing $SPODCAST_COMMAND in the feed manager

    New install requirements:

    • ffmpeg-python
    • setuptools

    The change to a SPA was necessitated by the introduction of the --transcode yes/no option which (when activated) causes feed updates to take much more time, especially on less powerful hardware. This would cause the feed manager process to timeout before the feeds were updated. This problem is mostly fixed but can still occur in the webcron update process. If this happens the php-fpm and/or web server timeout needs to be increased. This should only happen on slower hardware and/or slow links. See the README for information on how to do this for nginx.

    Source code(tar.gz)
    Source code(zip)
    spodcast-0.4.8.tar.gz(43.13 KB)
  • v0.4.6(Mar 2, 2022)

    Changes:

    • added --transcode yes/no to enable transcoding .ogg into .mp3 for handicapped devices which do not support open codes (iOS, looking at you here)
    • added webcron endpoint to run feed updates in situations where the system scheduler can not be used
    • feed manager is now mostly a single page app with live updates
    • added -v (version) option
    • added versioned updatees for feed and index manager

    Fixes:

    • direct login now works as intended

    New install requirements:

    • ffmpeg-python
    • setuptools

    The change to a SPA was necessitated by the introduction of the --transcode yes/no option which (when activated) causes feed updates to take much more time, especially on less powerful hardware. This would cause the feed manager process to timeout before the feeds were updated. This problem is mostly fixed but can still occur in the webcron update process. If this happens the php-fpm and/or web server timeout needs to be increased. This should only happen on slower hardware and/or slow links. See the README for information on how to do this for nginx.

    Source code(tar.gz)
    Source code(zip)
    spodcast-0.4.6.tar.gz(43.08 KB)
  • v0.4.3(Mar 2, 2022)

    Changes:

    • added --transcode yes/no to enable transcoding .ogg into .mp3 for handicapped devices which do not support open codes (iOS, looking at you here)
    • added webcron endpoint to run feed updates in situations where the system scheduler can not be used
    • feed manager is now mostly a single page app with live updates
    • added -v (version) option
    • added versioned updatees for feed and index manager

    Fixes:

    • direct login now works as intended

    New install requirements:

    • ffmpeg-python
    • setuptools

    The change to a SPA was necessitated by the introduction of the --transcode yes/no option which (when activated) causes feed updates to take much more time, especially on less powerful hardware. This would cause the feed manager process to timeout before the feeds were updated. This problem is mostly fixed but can still occur in the webcron update process. If this happens the php-fpm and/or web server timeout needs to be increased. This should only happen on slower hardware and/or slow links.

    Source code(tar.gz)
    Source code(zip)
    spodcast-0.4.3.tar.gz(42.86 KB)
  • v0.3.7(Feb 23, 2022)

  • v0.3.6(Feb 23, 2022)

  • v0.3.5(Feb 16, 2022)

  • v0.3.3(Feb 15, 2022)

  • v0.3.2(Feb 15, 2022)

  • v0.3.1(Feb 14, 2022)

Owner
Frank de Lange
I mostly use my own code repository but occasionally dump stuff to Github - Internet was meant to be decentralised. Nae lairds, nae kings, we are free men
Frank de Lange
A tool for transferring server variable values from one intersect gamedata.db to another

Server Variable Transfer Tool Purpose This tool exists for use with the Intersect Engine (Ascension Game Dev GitHub). Its purpose is to UPDATE one sql

AVild 2 Oct 27, 2021
Unarchive Bot for Telegram

Telegram UnArchiver Bot UnArchiveBot: 🇬🇧 Bot that allows you to extract supported archive formats in telegram. 🇹🇷 Desteklenen arşiv biçimleri tele

Hüzünlü Artemis [HuzunluArtemis] 25 May 07, 2022
The Social-Engineer Toolkit (SET) is specifically designed to perform advanced attacks against the human element.

The Social-Engineer Toolkit (SET) The Social-Engineer Toolkit (SET) is specifically designed to perform advanced attacks against the human element. SE

Professor 6 Nov 28, 2022
Python3 program to control Elgato Ring Light on your local network without Elgato's Control Center software

Elgato Light Controller I'm really happy with my Elgato Key Light from an illumination perspective. However, their control software has been glitchy f

Jeff Tarr 14 Nov 16, 2022
Telegram tools

Telegram-Tools Telegram tools. Explanation English | 中文 Features Export group memebrs Add users to the group Send message to users Setup API Open http

4 Apr 02, 2022
Scripts to help you win the Pizza Express

Slice of the Prizes Slice of the Prizes is a Python Script designed to enter the "Slice of the Action" competition hosted by Pizza Express the competi

Luke Bendall 1 Nov 04, 2021
Gets instagram public username and returns usefull informations like profilepic(b64), video_urls etc.

InstaSucker Gets instagram public username and returns usefull informations like profilepic(b64), video_urls etc. Information this project contains a

Armin Amiri 5 Apr 30, 2022
Search stock images (e.g. via Unsplash) and save them to your Wagtail image library.

Wagtail Stock Images Search stock images (e.g. via Unsplash) and save them to your Wagtail image library. Requirements Python 3 Django = 2 Wagtail =

Vicktor 12 Oct 12, 2022
Enigma simulator with python and clean code.

Enigma simulator with python and clean code.

Mohammad Dori 3 Jul 21, 2022
Lumberjack-bot - A game bot written for Lumberjack game at Telegram platform

This is a game bot written for Lumberjack game at Telegram platform. It is devel

Uğur Uysal 6 Apr 07, 2022
Um painel de consultas completo, com metodos atualizados.

Meu pix para eu comprar um café :D "25ef499b-d184-4aa1-9797-0a294be40d83" Painel-de-Consultas Completo. Feito por JOESTAR-TEAM Painel de consultas Com

Dio brando 10 Nov 19, 2021
A Python wrapper around the OpenWeatherMap web API

PyOWM A Python wrapper around OpenWeatherMap web APIs What is it? PyOWM is a client Python wrapper library for OpenWeatherMap (OWM) web APIs. It allow

Claudio Sparpaglione 740 Dec 18, 2022
数字货币BTC量化交易系统-实盘行情服务器,虚拟币自动炒币-火币API-币安交易所-量化交易-网格策略。趋势跟踪策略,最简源码,可在线回测,一键部署,可定制的比特币量化交易框架,3年实盘检验!

huobi_intf 提供火币网的实时行情服务器(支持火币网所有交易对的实时行情),自带API缓存,可用于实盘交易和模拟回测。 行情数据,是一切量化交易的基础,可以获取1min、60min、4hour、1day等数据。数据能进行缓存,可以在多个币种,多个时间段查询的时候,查询速度依然很快。 服务框架

dev 258 Sep 20, 2021
Telegram 隨機色圖,支援每日自動爬取

Telegram 隨機色圖機器人 使用此原始碼的Bot 開放的隨機色圖機器人: @katonei_bot 已實現的功能 爬取每日R18排行榜 不夠色!再來一張 Tag 索引,指定Tag色圖 將爬取到的色圖轉為 WebP 格式儲存,節省空間 需要注意的事件 好久之前的怪東西,代碼質量不保證 請在使用A

cluckbird 15 Oct 18, 2021
A Flask inspired, decorator based API wrapper for Python-Slack.

A Flask inspired, decorator based API wrapper for Python-Slack. About Tangerine is a lightweight Slackbot framework that abstracts away all the boiler

Nick Ficano 149 Jun 30, 2022
A Python wrapper for the DeepL API

deepl.py A Python wrapper for the DeepL API installing Install and update using pip: pip install deepl.py A simple example. # Sync Sample import deep

grarich 18 Dec 12, 2022
A fast and expressive Craigslist API wrapper

pycraigslist A fast and expressive Craigslist API wrapper. ⚠ As of September 2021, it is believed that Craigslist added a rate-limiter. It is advised

Ira Horecka 24 Dec 28, 2022
DEPRECATED - Official Python Client for the Discogs API

⚠️ DEPRECATED This repository is no longer maintained. You can still use a REST client like Requests or other third-party Python library to access the

Discogs 483 Dec 31, 2022
Pixiv 爬虫,使用 Python 实现。支持批量下载、上传到图床。

用 Python 实现的 Pixiv 爬虫,支持批量下载和上传。 随机图片 API: https://loliapi.ml/ Deploy Github Action 集成部署 建议使用本方法部署,相较于本地部署,无需搭建环境,全程在线上完成。并且使用国外服务器下载、上传,网络更加通畅。 Fork

18 Feb 26, 2022