Online Choir is online rehearsal tool.
Unlike most software of this kind, it does not require a low-latency or broad internet connection.
It works with pretty much any connection, including mobile.
Of course, it does not either offer a full two-way audio link between musicians.
Instead, it allows a single user (usually the director) to hear all musicians singing (or playing) together
in a nice live _synchronized_ mix.
The package offers three executables:
* The master client (aka director app) to be used by the choir director.
* The slave client (aka singer app) to be used by the singers (or any musician)
* The server, which handles the communication between the clients.
# Installation
_online-choir_ is a Python package on Pypi and hence can be installed on any platform equipped with Python
(greater than 3.7) and `pip`:
````shell
pip3 install online-choir
````
However, some of its dependencies require (on some platforms) that some libraries are present.
Therefore, we make the following recommendations for specific operating systems:
## Stand-alone apps
On MacOS and Windows, we recommend the stand-alone apps, which do not require Python or any other library to be
installed on the host computer.
They can be downloaded under the following likns:
* [Director App Mac](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/download?job=mac-director-app)
* [Director App Windows (installer)](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/src/Output/online-choir-director-setup.exe?job=win-app)
* [Director App Windows (stand-alone app)](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/src/dist/Online\%20Choir\%20Director.exe?job=win-app)
* [Singer App Mac](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/download?job=mac-singer-app)
* [Singer App Windows (installer)](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/src/Output/online-choir-singer-setup.exe?job=win-app)
* [Singer App Windows (stand-alone app)](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/src/dist/Online\%20Choir\%20Singer.exe?job=win-app)
Please refer to the user manual for instructions on how to use the apps:
* [Master client user manual](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/docs/user_manual_director.pdf?job=documentation)
* [Slave client user manual](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/docs/user_manual_singer.pdf?job=documentation)
Note that that is no stand-alone app for the server part.
## MacOS
If you don't want to use the stand-alone apps or want to run the server on MacOS, you can of cource install the Python
package instead.
We do recommend to use [Homebrew](https://brew.sh) for installing Python and the required libraries on MacOS;
Please follow the installation instructions from Homebrew first.
Then, issue the following command to install Python and the required libraries:
```shell
brew install python-tk portaudio libsndfile faad2
```
Then proceed with the usual installation of the package using `pip3` (see above).
## Debian/Ubuntu
In order to use the package, you need to install the following first:
```shell
apt install python3-tk python3-pip python3-numpy python3-soundfile libportaudio2 libsndfile1
```
Then proceed with the usual installation of the package using `pip` (see above).
# Usage
## Rehearsing with online-choir
In order to use _online-choir_, you need a server instance to run on a machine that is reachable from all clients.
Then, each singer needs to run the slave client on their computer, and have headphones and a microphone attached to it.
Finally, the director uses the master client to play some audio track that all singers hear.
Singers sing along the audio track, and in return the director hears all singers (optionally together with the audio
track).
The main limitation of the software is that the singers do not hear each other.
As a counterpart for this limitation, the software works with nearly any internet connection, even if some have high
latencies. Latency and jitter *do not* disturb the synchronicity of the mix.
This means that the voice of signers with high-latency connections come exactly at the same time to the director as
those of singers with low-latency connections.
### Live session
The audio track played to the singers is typically read from a pre-recorded audio file provided by the director (e.g.
a karaoke-style instrumental track).
Alternatively, the input audio file can be replaced by a direct streaming from the director's local computer.
In this mode, the choir is not played back immediately to the director (mute mode).
The director should listen to the recoreded mix after the session instead.
## Running the software
Once installed, the package provides three commands:
* `online-choir-director`
* `online-choir-singer`
* `online-choir-server`
each corresponding to one piece of the software.
If you did not install the package via pip and only downloaded the source code, you can invoke the same three scripts
using the following commands from the root directory of the project:
* `python3 bin/run_master_client.py`
* `python3 bin/run_slave_client.py`
* `python3 bin/run_server.py`
### Common options
All three commands accept the following options:
* `--log-level [LEVEL]` sets the logging level in the terminal (logs at all levels are anyways recorded into the log
file, see below)
* `--log-file [LOG_FILE]` determines where to record the logs as well as the content of `stderr`.
By default, the log file is:
* MacOS: `~/Library/Logs/Online Choir/<script name>.log`
* Windows: `%APPDATA%\Online Choir\<script name>.log`
* Linux: `~/.online-choir/<script name>.log`
* `--console` prevents the content of `stderr` from being re-routed into the log file.
### Master client options
`--no-gui` runs the master client in CLI mode. Otherwise, it runs in GUI mode. For more detail on how to use the GUI,
please refer to the [user manual](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/documentation/user_manual_director.pdf?job=documentation).
In CLI mode, each invocation of the client starts a singing session. The client exists after each session.
The following options are available:
* `--input [INPUT_FILE]` specifies the audio file that is played to the singers. The file needs to be in WAV format,
stereo, with 44.1kHz sampling rate and 16 bits per sample.
* `--live` alternatively to playing from a file, this option allows to send the computer's default audio input to the
singers (see section on live sessions below)
* `--mute` do not play the mix aloud (implied by `--live`)
* `--output [OUTPUT_FILE]` records the mix into the given file (either in WAV or FLAC format, depending on the
extension)
* `--server [SERVER_ADDRESS]` the hostname of the server
* `--port [PORT_NUMBER]` the port number on the server (default is 8878)
* `--skip-seconds [NUMBER_OF_SECONDS]` if playing from a file, skip this amount of seconds at the beginning of the file
* `--monitor-level [LEVEL]` the level of the input file in the mix
### Slave client options
`--no-gui` runs the master client in CLI mode. Otherwise, it runs in GUI mode. For more detail on how to use the GUI,
please refer to the [user manual](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/documentation/user_manual_singer.pdf?job=documentation).
In CLI mode, each invocation of the client starts a singing session. The client exists after each session.
The following options are available:
* `--server [SERVER_ADDRESS]` the hostname of the server
* `--port [PORT_NUMBER]` the port number on the server (default is 8868)
* `--latency [LATENCY]` specifies the latency of the audio devices on the host computer. This should be an integer.
Each unit corresponds to 11.6ms (e.g. a value of 5 means 58ms). The GUI mode provides a tool to measure this latency.
### Server options
* `--master-port` [PORT_NUMBER] the port to which master clients should connect
* `--slave-port` [PORT_NUMBER] the port to which slave clients should connect
* `--control-port`[PORT_NUMBER] the port to which master clients should connect for controlling the mix. The master
client always uses <mater port> + 20. Therefore, if you configure this option to anything else than <master-port>
+ 20, the mixing desk feature of the master client will not work.
* `--ices` see section below.
### Web Radio
Optionally the server script can start an instance of `ices2` (needs to be installed separately) in order to stream the
input file to an IceCast server.
This is useful when some singers are unable to use the slave client; as a replacement, they can get the same audio cue
from the web radio and participate in the rehearsal.
This option is activated by the `--ices <ices_config>` option of the server script. It requires a valid config file for
`ices2`.
# Annex
## Description of the network protocol
### General rules
The following rules apply to all communication among programs:
* Raw TCP channels with fixed sized transmission chunks: the programs communicate directly through TCP, and use the
`readexactly()` method to await data chunks, as their size is predefined.
* Immediately after establishing the TCP connection to the server, clients (both master and slave) send a predefined
authentication frame (see `common/auth.py`) to the server. If anything else is sent first, the server drops the
connection.
* After authentication, client and server communicate by sending fixed-sized chunks to each-other. The chunk sizes are
the following:
* master to server: 514 bytes
* server to master 1028 bytes
* slave to server: 260 bytes
* server to slave: 514 bytes
* The first 4 bytes of the chunks are always carrying a "chunk index" (a 32-bit signed integer), and the rest is
payload.
* The chunk index must be a multiple of 199 (prime number). In the sequel we omit this multiplier for clarity, i.e.
chunk no. 3 would actually be sent as number 3 * 199 = 597.
* The payload is either empty or contains audio signal. If there is any audio content, it is invariably 512 audio
frames.
The type of encoding determines the actual payload size (4 bytes less than the packet size):
* master to server and server to slave: stereo ADPCM, 512 bytes
* slave to server: mono ADPCM, 256 bytes
* server to master: raw mono PCM, 1024 bytes
* Chunks containing actual audio have positive chunk indices (including 0), while negative indices are for signalling.
### Master-server protocol
The master client only establishes a connection to the server when the session starts.
After the authentication packet has been sent, the master client proceeds and sends the audio content directly.
When it wants to end the session (either because there is no audio left to send, or because the user presses the Stop
button), it terminates the session with a chunk with index -1 (remember that all indices are actually multiplied by 199
in the chunk's data, hence the chunk begins with -199 encoded in its 4 first bytes; the rest of the chunk's payload does
not matter).
On the downlink, the server sends the mix back to the client.
It uses no signalling chunk at all and just proceeds with audio chunks as soon as they are available.
The master client waits until it receives exactly as many audio chunks as it sent to the server.
After receiving all the chunks, the client closes the connection with the server.
### Slave-server protocol
The slave client connects to the server as soon as it is opened, sends its authentication paket and then waits for audio
content to come from the server.
If the connection is idle for more than 10 seconds, it sends a keep-alive chunk to the server.
Keep-alive chunks are chunks with index -3.
Likewise, the server will send keep-alive chunks on a regular basis to the client.
The client shall drop the connection if no chunk at all has arrived in the last 30 seconds.
The client knows that a session starts when it receives a chunk with index 0.
Immediately after receiving that chunk, the client acknowledges that it will participate in the session by sending back
a chunk with index -1 (aka check-in chunk).
Then it proceeds and start sending its audio stream (the participant's voice) as soon as available.
THE SLAVE CLIENT IS RESPONSIBLE FOR SYNCHRONIZING ITS AUDIO STREAM WITH THAT IT RECEIVED.
That means that the audio content it sends with index i shall contain the audio recorded exactly when chunk number i was
played to the ears of the participant.
The server send a chunk with index -1 to signal the end of the session.
The salve client does not need to signal the end of its audio stream, as the server expects to receive exactly as many
chunks from the slave as it sent to it.
If for technical reasons the slave needs to drop from the session (for example, it experienced a playback buffer
under-run, causing the singing to be interrupted and no longer smooth), it sends a chunk with index -2 to signal the
error.
The server then knows that is shall not expect further audio from that client and will ignore it in the mix.
## Credits
Icon by [Muhammad Haq](https://freeicons.io/profile/823) on [freeicons.io](https://freeicons.io)
Raw data
{
"_id": null,
"home_page": "https://gitlab.com/odousse/online-choir",
"name": "online-choir",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "",
"keywords": "",
"author": "Olivier Dousse",
"author_email": "olivier@modetexte.ch",
"download_url": "https://files.pythonhosted.org/packages/d1/0b/8c16129053ee9a66c6a7e5903747681363c4c08c059107d7d1c5800cce98/online-choir-1.1.3.tar.gz",
"platform": null,
"description": "Online Choir is online rehearsal tool. \nUnlike most software of this kind, it does not require a low-latency or broad internet connection.\nIt works with pretty much any connection, including mobile.\n\nOf course, it does not either offer a full two-way audio link between musicians. \nInstead, it allows a single user (usually the director) to hear all musicians singing (or playing) together \nin a nice live _synchronized_ mix.\n\nThe package offers three executables:\n * The master client (aka director app) to be used by the choir director.\n * The slave client (aka singer app) to be used by the singers (or any musician)\n * The server, which handles the communication between the clients.\n\n# Installation\n\n_online-choir_ is a Python package on Pypi and hence can be installed on any platform equipped with Python \n(greater than 3.7) and `pip`:\n````shell\npip3 install online-choir\n````\nHowever, some of its dependencies require (on some platforms) that some libraries are present.\nTherefore, we make the following recommendations for specific operating systems:\n\n## Stand-alone apps\n\nOn MacOS and Windows, we recommend the stand-alone apps, which do not require Python or any other library to be \ninstalled on the host computer. \nThey can be downloaded under the following likns:\n * [Director App Mac](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/download?job=mac-director-app)\n * [Director App Windows (installer)](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/src/Output/online-choir-director-setup.exe?job=win-app)\n * [Director App Windows (stand-alone app)](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/src/dist/Online\\%20Choir\\%20Director.exe?job=win-app)\n * [Singer App Mac](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/download?job=mac-singer-app)\n * [Singer App Windows (installer)](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/src/Output/online-choir-singer-setup.exe?job=win-app)\n * [Singer App Windows (stand-alone app)](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/src/dist/Online\\%20Choir\\%20Singer.exe?job=win-app)\n\nPlease refer to the user manual for instructions on how to use the apps:\n * [Master client user manual](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/docs/user_manual_director.pdf?job=documentation)\n * [Slave client user manual](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/docs/user_manual_singer.pdf?job=documentation)\n\nNote that that is no stand-alone app for the server part.\n\n## MacOS\n\nIf you don't want to use the stand-alone apps or want to run the server on MacOS, you can of cource install the Python \npackage instead.\nWe do recommend to use [Homebrew](https://brew.sh) for installing Python and the required libraries on MacOS; \nPlease follow the installation instructions from Homebrew first.\nThen, issue the following command to install Python and the required libraries:\n```shell\nbrew install python-tk portaudio libsndfile faad2\n```\nThen proceed with the usual installation of the package using `pip3` (see above).\n\n## Debian/Ubuntu\n\nIn order to use the package, you need to install the following first:\n```shell\napt install python3-tk python3-pip python3-numpy python3-soundfile libportaudio2 libsndfile1\n```\nThen proceed with the usual installation of the package using `pip` (see above).\n\n# Usage\n\n## Rehearsing with online-choir\n\nIn order to use _online-choir_, you need a server instance to run on a machine that is reachable from all clients.\nThen, each singer needs to run the slave client on their computer, and have headphones and a microphone attached to it.\nFinally, the director uses the master client to play some audio track that all singers hear. \nSingers sing along the audio track, and in return the director hears all singers (optionally together with the audio \ntrack). \n\nThe main limitation of the software is that the singers do not hear each other.\nAs a counterpart for this limitation, the software works with nearly any internet connection, even if some have high\nlatencies. Latency and jitter *do not* disturb the synchronicity of the mix. \nThis means that the voice of signers with high-latency connections come exactly at the same time to the director as \nthose of singers with low-latency connections.\n\n### Live session\n\nThe audio track played to the singers is typically read from a pre-recorded audio file provided by the director (e.g.\na karaoke-style instrumental track).\nAlternatively, the input audio file can be replaced by a direct streaming from the director's local computer.\nIn this mode, the choir is not played back immediately to the director (mute mode).\nThe director should listen to the recoreded mix after the session instead.\n\n## Running the software\n\nOnce installed, the package provides three commands:\n * `online-choir-director`\n * `online-choir-singer`\n * `online-choir-server`\neach corresponding to one piece of the software.\n \nIf you did not install the package via pip and only downloaded the source code, you can invoke the same three scripts\nusing the following commands from the root directory of the project:\n * `python3 bin/run_master_client.py`\n * `python3 bin/run_slave_client.py`\n * `python3 bin/run_server.py`\n\n### Common options\n\nAll three commands accept the following options:\n * `--log-level [LEVEL]` sets the logging level in the terminal (logs at all levels are anyways recorded into the log \n file, see below)\n * `--log-file [LOG_FILE]` determines where to record the logs as well as the content of `stderr`.\n By default, the log file is:\n * MacOS: `~/Library/Logs/Online Choir/<script name>.log`\n * Windows: `%APPDATA%\\Online Choir\\<script name>.log`\n * Linux: `~/.online-choir/<script name>.log`\n * `--console` prevents the content of `stderr` from being re-routed into the log file.\n\n### Master client options\n\n`--no-gui` runs the master client in CLI mode. Otherwise, it runs in GUI mode. For more detail on how to use the GUI, \nplease refer to the [user manual](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/documentation/user_manual_director.pdf?job=documentation).\n\nIn CLI mode, each invocation of the client starts a singing session. The client exists after each session. \nThe following options are available:\n * `--input [INPUT_FILE]` specifies the audio file that is played to the singers. The file needs to be in WAV format, \n stereo, with 44.1kHz sampling rate and 16 bits per sample.\n * `--live` alternatively to playing from a file, this option allows to send the computer's default audio input to the\n singers (see section on live sessions below)\n * `--mute` do not play the mix aloud (implied by `--live`)\n * `--output [OUTPUT_FILE]` records the mix into the given file (either in WAV or FLAC format, depending on the \n extension)\n * `--server [SERVER_ADDRESS]` the hostname of the server\n * `--port [PORT_NUMBER]` the port number on the server (default is 8878)\n * `--skip-seconds [NUMBER_OF_SECONDS]` if playing from a file, skip this amount of seconds at the beginning of the file\n * `--monitor-level [LEVEL]` the level of the input file in the mix\n\n### Slave client options\n\n`--no-gui` runs the master client in CLI mode. Otherwise, it runs in GUI mode. For more detail on how to use the GUI, \nplease refer to the [user manual](https://gitlab.com/odousse/online-choir/-/jobs/artifacts/master/raw/documentation/user_manual_singer.pdf?job=documentation).\n\nIn CLI mode, each invocation of the client starts a singing session. The client exists after each session. \nThe following options are available:\n * `--server [SERVER_ADDRESS]` the hostname of the server\n * `--port [PORT_NUMBER]` the port number on the server (default is 8868)\n * `--latency [LATENCY]` specifies the latency of the audio devices on the host computer. This should be an integer.\n Each unit corresponds to 11.6ms (e.g. a value of 5 means 58ms). The GUI mode provides a tool to measure this latency.\n\n### Server options\n\n * `--master-port` [PORT_NUMBER] the port to which master clients should connect\n * `--slave-port` [PORT_NUMBER] the port to which slave clients should connect\n * `--control-port`[PORT_NUMBER] the port to which master clients should connect for controlling the mix. The master \n client always uses <mater port> + 20. Therefore, if you configure this option to anything else than <master-port> \n + 20, the mixing desk feature of the master client will not work.\n * `--ices` see section below.\n\n### Web Radio\n\nOptionally the server script can start an instance of `ices2` (needs to be installed separately) in order to stream the \ninput file to an IceCast server. \nThis is useful when some singers are unable to use the slave client; as a replacement, they can get the same audio cue \nfrom the web radio and participate in the rehearsal.\nThis option is activated by the `--ices <ices_config>` option of the server script. It requires a valid config file for\n`ices2`.\n\n# Annex\n\n## Description of the network protocol\n\n### General rules\n\nThe following rules apply to all communication among programs:\n * Raw TCP channels with fixed sized transmission chunks: the programs communicate directly through TCP, and use the \n `readexactly()` method to await data chunks, as their size is predefined.\n * Immediately after establishing the TCP connection to the server, clients (both master and slave) send a predefined \n authentication frame (see `common/auth.py`) to the server. If anything else is sent first, the server drops the \n connection.\n * After authentication, client and server communicate by sending fixed-sized chunks to each-other. The chunk sizes are\n the following:\n * master to server: 514 bytes\n * server to master 1028 bytes\n * slave to server: 260 bytes\n * server to slave: 514 bytes\n * The first 4 bytes of the chunks are always carrying a \"chunk index\" (a 32-bit signed integer), and the rest is \n payload.\n * The chunk index must be a multiple of 199 (prime number). In the sequel we omit this multiplier for clarity, i.e. \n chunk no. 3 would actually be sent as number 3 * 199 = 597.\n * The payload is either empty or contains audio signal. If there is any audio content, it is invariably 512 audio \n frames.\n The type of encoding determines the actual payload size (4 bytes less than the packet size):\n * master to server and server to slave: stereo ADPCM, 512 bytes\n * slave to server: mono ADPCM, 256 bytes\n * server to master: raw mono PCM, 1024 bytes\n * Chunks containing actual audio have positive chunk indices (including 0), while negative indices are for signalling.\n\n### Master-server protocol\n\nThe master client only establishes a connection to the server when the session starts. \nAfter the authentication packet has been sent, the master client proceeds and sends the audio content directly.\nWhen it wants to end the session (either because there is no audio left to send, or because the user presses the Stop \nbutton), it terminates the session with a chunk with index -1 (remember that all indices are actually multiplied by 199\nin the chunk's data, hence the chunk begins with -199 encoded in its 4 first bytes; the rest of the chunk's payload does\nnot matter).\n\nOn the downlink, the server sends the mix back to the client. \nIt uses no signalling chunk at all and just proceeds with audio chunks as soon as they are available. \nThe master client waits until it receives exactly as many audio chunks as it sent to the server.\nAfter receiving all the chunks, the client closes the connection with the server.\n\n### Slave-server protocol\n\nThe slave client connects to the server as soon as it is opened, sends its authentication paket and then waits for audio\ncontent to come from the server.\n\nIf the connection is idle for more than 10 seconds, it sends a keep-alive chunk to the server. \nKeep-alive chunks are chunks with index -3.\nLikewise, the server will send keep-alive chunks on a regular basis to the client. \nThe client shall drop the connection if no chunk at all has arrived in the last 30 seconds.\n\nThe client knows that a session starts when it receives a chunk with index 0. \nImmediately after receiving that chunk, the client acknowledges that it will participate in the session by sending back\na chunk with index -1 (aka check-in chunk).\nThen it proceeds and start sending its audio stream (the participant's voice) as soon as available.\nTHE SLAVE CLIENT IS RESPONSIBLE FOR SYNCHRONIZING ITS AUDIO STREAM WITH THAT IT RECEIVED. \nThat means that the audio content it sends with index i shall contain the audio recorded exactly when chunk number i was\nplayed to the ears of the participant. \n\nThe server send a chunk with index -1 to signal the end of the session.\nThe salve client does not need to signal the end of its audio stream, as the server expects to receive exactly as many \nchunks from the slave as it sent to it.\n\nIf for technical reasons the slave needs to drop from the session (for example, it experienced a playback buffer \nunder-run, causing the singing to be interrupted and no longer smooth), it sends a chunk with index -2 to signal the\nerror. \nThe server then knows that is shall not expect further audio from that client and will ignore it in the mix. \n\n## Credits\n\nIcon by [Muhammad Haq](https://freeicons.io/profile/823) on [freeicons.io](https://freeicons.io)\n",
"bugtrack_url": null,
"license": "",
"summary": "A tool to sing together online",
"version": "1.1.3",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "eb825bec0817199c220d8620c38193c4",
"sha256": "8de1b3c4ef6817517217eb5aa84c6a82d9f2240115322a01c77280d37f7680ec"
},
"downloads": -1,
"filename": "online_choir-1.1.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "eb825bec0817199c220d8620c38193c4",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 631493,
"upload_time": "2022-12-19T19:25:59",
"upload_time_iso_8601": "2022-12-19T19:25:59.064675Z",
"url": "https://files.pythonhosted.org/packages/27/84/1004fec8cd7e97f213084140c3694d297eecb2fed7c3c5f019f9fb83e4ec/online_choir-1.1.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "9e7407e6fb44afdea08618a2fc187204",
"sha256": "4bf304d97d69970576d99371a3aab22ab00fbca7b119f473ac582ffee80ecb4d"
},
"downloads": -1,
"filename": "online-choir-1.1.3.tar.gz",
"has_sig": false,
"md5_digest": "9e7407e6fb44afdea08618a2fc187204",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 620837,
"upload_time": "2022-12-19T19:26:00",
"upload_time_iso_8601": "2022-12-19T19:26:00.884224Z",
"url": "https://files.pythonhosted.org/packages/d1/0b/8c16129053ee9a66c6a7e5903747681363c4c08c059107d7d1c5800cce98/online-choir-1.1.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-12-19 19:26:00",
"github": false,
"gitlab": true,
"bitbucket": false,
"gitlab_user": "odousse",
"gitlab_project": "online-choir",
"lcname": "online-choir"
}