{ "info": { "author": "Mark Michon", "author_email": "markmichon7@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.7" ], "description": "[![Downloads](https://pepy.tech/badge/bitglitter)](https://pepy.tech/project/bitglitter)\n\n**[Discord Server](https://discord.gg/t9uv2pZ)**\n\n![BitGlitter Logo](https://i.imgur.com/3GJ4bDx.png)\n\n# The basics\n\n![BitGlitter Sample GIF](https://i.imgur.com/n7E7lnd.gif)\n\n**[Click here](https://www.youtube.com/watch?v=HrY4deFrOoA) for a demo video of a real stream.**\n\nBitGlitter is an easy to use library that allows you to embed data inside of ordinary pictures or video. Store and host\nfiles wherever images or videos can be hosted.\n\n### From physical barcodes to digital data transfer\n\nWhether it's barcodes at the store or QR codes you can scan with your phone- they both work on the same principle. Data\nis encoded into the black and white. You can think of each color as an abstraction for a binary value, so then when\nthose colors are read in sequence, you can pull meaningful data from the image. I wondered how this concept could be\nimproved upon, and I wanted a cool first project as an introduction to programming. BitGlitter was born.\n\nConventional barcodes are severely limited in application, in terms of their data density. When you maximize the \nconcept and configure it for digital-digital transmission, a lot of capability is gained. \nBitGlitter is in a class of it's own in several ways:\n\n![BitGlitter Default Palettes](https://i.imgur.com/dSYmq7V.png)\n\n+ **Multiple Color Palettes:** By removing the constraint of only using black and white, the amount of data you can \nhold in a given \"block\" (each square) on the frame skyrockets. The regular two color setup holds one bit per block. \nFour colors holds two bits (2x), sixty four colors holds six bits (6x), and lossless ~16.7M color palette holds 24 bits \n(24x improvement over black & white). You choose what color palette you'd like to use according to your application. \nSmaller sets are far resistant to compression and corruption, while larger sets have higher data densities. You can\ncreate your own custom palettes as well with whatever colors you'd like to use. More about that below.\n+ **Multi-Frame Videos:** BitGlitter automatically breaks up larger files into multiple frames, with several headers\nbuilt into them. These include several layers of protection against corruption, as well as metadata about the frame\nas well as the stream itself so the reader can intelligently decide how to handle it. By stitching these frames\n together and turning them into a video, you can embed files and folders of arbitrary size into videos and images. \nYou're only limited by your hard drive.\n+ **Variable block size:** Each of the blocks in the frame can be set to any size, including one pixel. Larger block \nsizes give your stream protection in lossy environments, while smaller blocks allow for greater densities.\n\nCurrently, BitGlitter is configured to transport files and folders on a computer. But with minimal modification you can\nuse videos or images as a carrier for virtually any kind of binary data.\n\n### What this means in practical terms\n\nHere's some real data that gives you an idea of what is possible with this:\n\nNumber of Colors | Bits Per Block | Screen Resolution | Block Size in Pixels | Block Dimensions | Framerate | Throughput | Lossless Application\n--- | --- | --- | --- | --- | --- | --- | ---\n2 | 1 | 640 x 480 (480p) | 20 | 32 x 24 | 30 | 2.88 KB/s | No\n4 | 2 | 1280 \u00c3\u2014 720 (720p) | 20 | 64 x 36 | 30 | 17.28 KB/s | No\n8 | 3 | 1280 \u00c3\u2014 720 (720p) | 20 | 64 x 36 | 30 | 25.92 KB/s | No\n16 | 4 | 1920 x 1080 (1080p) | 20 | 96 x 54 | 30 | 77.76 KB/s | No\n64 | 6 | 1920 x 1080 (1080p) | 20 | 96 x 54 | 30 | 116.64 KB/s | No\n64 | 6 | 1920 x 1080 (1080p) | 20 | 96 x 54 | 60 | 233.28 KB/s | No\n16,777,216 | 24 | 1920 x 1080 (1080p) | 5 | 384 x 216 | 30 | 7.47 MB/s | Yes\n16,777,216 | 24 | 3840 x 2160 (4k) | 5 | 768 x 432 | 60 | 59.7 MB/s | Yes\n\nPut simply, you can now make videos that can hold large amounts of data inside of them. There may be some pretty\ninteresting applications that can come out of this.\n\n# Features\n\n### Data\n\n+ **Supports streams up to ~1 EB in size, or ~4.3B frames:** In other words, there is no practical limit to the\n stream's size.\n+ **Compression added:** This is done automatically, so don't worry about putting your files in a rar or zip prior to\nsending.\n+ **Encryption added:** Optional AES-256 encryption to protect your files. Passwords are hashed with `scrypt`, \nparameters can be customized for your needs.\n+ **File masking:** Optional ability to mask what files are included in the stream. Only those who successfully grab \nthe stream (and decrypt it if applicable) will know of its contents.\n\n### Outputted Files\n\nYou can choose between either outputting all of your frames as a series of images (.png), or as a single .mp4.\n\n+ **Customizable resolution:** You have complete control of the size of the outputted frames, whether they are 480p or\n8K.\n+ **Customizable framerate:** Currently supports 30 and 60 FPS, custom values are coming soon.\n\n![Custom Color Showcase](https://i.imgur.com/4uQTxwT.png)\n\n+ **Custom Color Palettes:** The included default palettes are just a starting point. Make any color palette that you\nwant to match the aesthetic where it's being used. Anyone reading the stream will have the palette automatically saved\nto their machine, so then they can use it as well! Functionality is included to output a text file outlining all of the\ncolor palettes you have available to use, both default and custom.\n\n### Reading\n\n+ **Error correction against compression or corruption:** BitGlitter protects your file against corruption and artifacts \non the image or video. After loading the correct palette, whenever it detects an incorrect color, it will \"snap\" it to \nthe nearest color in the palette. This gives your file resistance against format changes, codecs, or file size \nreduction. This allows BitGlitter streams to still be read in environments that would otherwise render all existing \nsteganography methods unreadable.\n\n+ **Complete file integrity:** When the stream is created, a hash (SHA-256) is taken of the entire stream, as\nwell as each frame. The data must match what is expected to be accepted. Damaged or corrupt files will not be blindly\npassed on to you.\n\n+ **Streamlined frame bypassing:** If a frame cannot or doesn't need to be read (ie, a duplicate already read), the \nreader determines this from an initial frame header\n\n### Design\n\n+ **No metadata saved in the file:** Compress the stream, change formats, upload it somewhere. All data is encoded in\nthe blocks, so you don't have to worry (as much) about rendering the stream unreadable.\n\n+ **Easy to understand:** Whether you're learning about Python and want to understand how it works, or you're looking to\ncontribute, docstrings and notes are throughout the library.\n\n+ **Built in future-proofing:** As of now, BitGlitter has a single protocol (Protocol 1), which is a specific set of\n procedures around how data is handled, and the components of a frame, as well as their layout. Each protocol has its\n own unique ID to identify it with. This ID is added in the header during the write process, and is picked up at \n `read()`. As new protocols get created, older versions of BitGlitter that don't have these included will notify the user\n to update their version in order for it to be read. All older protocol versions are saved in future library\n iterations, so no matter how old the protocol version is used on the stream, it will always be able to be read.\n\n+ **Fully modular design:** Do you have a specialized use case? Adapting this library to your own needs is quite easy.\n I've built BitGlitter to be easy to modify and expand upon. Rather than worrying about the lower-level functionality,\n achieve your goal with the modular components I've created.\n\n### Applications\nTo be determined. This will be updated as time progresses!\n\n# How to use\n\nAll of the functions to run this library are explained below. I'm also working on several Wikipedia pages, explaining\nBitGlitter in greater detail (how it works, etc) with some included illustrations. These are not yet complete, \n[but here is the link to the project's Wiki if you'd like to see it!](https://github.com/MarkMichon1/BitGlitter/wiki/Using-BitGlitter)\n\n### Installation\n\nIn addition to downloading the code from Github, you can also grab it directly from PyPI:\n\n`pip install bitglitter`\n\n**IMPORTANT:** The only part you will need to grab manually is a copy of ffmpeg.exe . Place it in the same folder the \ncode will be running, and you'll be set. This will be done automatically in the near future. Get the package here on\nthe left side of the screen:\n\n[https://ffmpeg.org/download.html](https://ffmpeg.org/download.html)\n\nffmpeg.exe is all that is needed.\n\n**Required Third Party Libraries**\n\n+ `bitstring` - Bit manipulation.\n+ `cryptography` - Cryptographic functions.\n+ `ffmpeg-python` - Video rendering and output.\n+ `opencv-python` - Video loading and frame manipulation.\n+ `Pillow` - Frame creation and output, as well as loading images and reading pixel values.\n\nThanks to Tanmay Mishra for giving me a pre-release version of his upcoming library `filepackager`. It has been heavily\nmodified and stripped down to suit this library. The code is included with BitGlitter; there is no need to download it.\n\n**BitGlitter in 60 seconds**\n\nEven though it comes shipped with a lot of functionality, all you need to use it is `write()` (creates the streams) and \n`read()` (which reads them and extracts the data encoded in it). The only required argument for both is the file you\nwish to input in string format.\n\n### write(), converting files into BitGlitter streams\n\nWe'll go a bit more in depth now.\n\n`write()` is the function that inputs files and turns them into a BitGlitter stream. There are quite a few arguments\nto customize the stream, but there is only one required argument. Everything else has defaults.\n\nNot surprisingly, that required argument defines what files or folders you wish to embed in the stream. If and only if\nyou're sending a single file or folder path, argument `fileList` takes a string of the path. BitGlitter also supports\nsending multiple files and folder together, of which there is no limit! This would require using a tuple or list item\nfilled with strings of the file or folder paths. File or folder paths that don't exist are automatically ignored.\n\n`streamName=''` is what you can use to optionally title your stream, which will be printed out on the screen of whoever\nreads the file, along with other stream data.\n\n`streamDescription=''` serves as a text field to optionally put a description for the stream.\n\n`outputPath=False` is where you can optionally define the path of where the created media is outputted. By\ndefault, media is saved where the python file is ran. The folder path must already exist if used.\n\n`outputMode='video'` is where you define how you wish the stream to output, whether as an .mp4 video, or a series of\n.png images. The only two valid arguments are `'image'` and `'video'`.\n\n`compressionEnabled=True` enables or disables compression of your data, prior to rendering into frames. This is enabled\nby default.\n\n`fileMaskEnabled=False` is where you can omit the listing of files/folders from the stream header. This effectively\nhides the contents of the stream, unless it is fully read. By default, this is disabled. What this means is when\nsomeone reads your stream, in the first several frames it will automatically display the contents of the stream (files\nas well as their size) on the screen.\n\n`encryptionKey=''` optionally encrypts your data with AES-256. By default, this is disabled. The stream will not be\nable to be read unless the reader successfully inputs this.\n\nArguments `scryptN=14`, `scryptR=8` and `scryptP=1` allow you to customize the parameters of the `scrypt` key derivation \nfunction. If you're a casual user, you'll never need to touch these (and shouldn't). Only change these settings if\n you're comfortable with cryptography and you know what you're doing! It's worth noting `scryptN` uses it's argument as\n 2^n. Finally, if you're changing these numbers, they MUST be manually inputted during `read()` otherwise decryption\n will fail! Custom values are deliberately not transmitted in the stream for security reasons. Your end users of the\n stream must know these custom parameters.\n\n`headerPaletteID='6'` sets the palette used in the 'setup' frames in the beginning. It is strongly recommended you use\na default palette here if you don't know what you're doing, because this is where important information regarding the\nstream is read, and by using a custom palette, it will be impossible for anyone to read it who hasn't already 'learned'\nthe palette.\n\n`streamPaletteID='6'` sets the palette used for the payload. By default, the 4 bit default color set is used. I'll \nexplain all about palettes below.\n\n`pixelWidth=24` sets how many pixels wide each block is when rendered. By default it's 20 pixels. This is a very\nimportant value regarding readability. Having them overly large will make reading them easier, but will result in less\nefficient frames and require substantially longer streams. Making them very small will greatly increase their\nefficiency, but at the same time a lot more susceptible to read failures if the files are shrunk, or otherwise\ndistorted.\n\n`blockHeight=45` sets how many blocks tall the frame will be, by default this is set to 45 (which along with \n`blockWidth`, creates a perfect 1080p sized frame).\n\n`blockWidth=80` sets how many blocks wide the frame will be. By default this is set to 96.\n\n`framesPerSecond=30` sets how many frames per second the video will play at, assuming argument `outputMode = \"video\"`.\nCurrently, 30fps and 60fps are accepted.\n\nFinally we have several arguments to control logging.\n\n`loggingLevel='info'` determines what level logging messages get outputted. It accepts three arguments- `info` is\ndefault and only shows core status data during `read()` and `write()`. `'debug'` shows INFO level messages as well as\nlower level messages from the various processes. Boolean `False` disables logging altogether.\n\n`loggingScreenOutput=True` sets whether logging messages are displayed on the screen or not. Only accepts type `bool`. \nEnabled by default.\n\n`loggingSaveOutput=False` determines whether logging messages are saved as text files or not. Only accepts type `bool`.\nDisabled by default. If set to `True`, a log folder will be created, and text files will be automatically saved there.\n\nThese default values have an 81KB/s transmission rate. This is only a starting point that should be pretty resistant to\ncorruption.\n\n### read(), converting streams back into data\n\n`read()` is what you use to input BitGlitter streams (whether images or video), and will output the files.\n\nLike with `write()`, the only argument required is the BitGlitter-encoded file, whether that's an image or a video.\n`fileToInput` is the only required argument. We'll go over the other ones.\n\n`outputPath=None` Is where you can set where this stream will be saved once all frames have been successfully loaded.\n It's 'set and forget', so if you are loading images this argument only has to be used once, and the folder path will\n stick with that stream. This argument requires a strong of a folder path that already exists.\n\n`badFrameStrikes=10` This sets how many corrupted frames the reader is to detect before it aborts out of a video. This\nallows you to break out of a stream relatively quickly if the video is substantially corrupted, without needing to\n iterate over each frame. If this is set to 0, it will disable strikes altogether, and attempt to read each frame \n regardless of the level of corruption.\n\n`blockHeightOverride=False` and `blockWidthOverride=False` allow you to manually input the stream's block height and \nblock width. Normally you'll never need to use this, as these values are automatically obtained as the frame is locked\nonto. But for a badly corrupted or compressed frame, this may not be the case. By using the override, the reader will\nattempt to lock onto the screen given these parameters. Both must be filled in order for the override to work.\n\n`encryptionKey=None` is where you add the encryption key to decrypt the stream. Like argument `outputPath`, you only\nneed this argument once, and it will bind to that save.\n\nArguments `scryptN=14`, `scryptR=8` and `scryptP=1`\n\n`loggingLevel = 'info'`, `loggingScreenOutput = True`, `loggingSaveOutput = False` - Please see the full explanation at\n`write().`\n\n### Color Palettes\n\nIf you wish to make your own custom color palettes, BitGlitter gives you the ability to do that with these functions.\n\n`addCustomPalette(paletteName, paletteDescription, colorSet, optionalNickname = \"\")` This function adds custom palettes\nto use. \n\nArgument `paletteName` takes a string and is the name of the palette that gets displayed and transmitted. \n\nArgument`paletteDescription` takes a string as well, and is the description of the palette, if you wish to add it. \n\nArgument `colorSet` takes a tuple of RGB tuples, these will be the actual colors used in the BitGlitter stream. Here's \na simple example of what it would look like using two colors: `colorSet=((0, 255, 0), (0, 0, 255))`. There are a few\nrequirements to these tuples:\n+ No two identical values can be added. For instance, the color black with the same RGB values twice. Each color used\nmust be unique! The more 'different' the colors are, the better.\n+ You must have a minimum of two colors.\n+ It must be 2^n colors used, so 2, 4, 8, 16, etc.\n\nArgument `optionalNickname=\"\"` allows you to use an easy to input nickname for your custom palette. This nickname is \n how you select this palette to specifically run on your stream. Internally, custom palettes have a 64 character ID \n code which you can use (more on this below). This allows you to give it a string of your choosing to designate it as \n well. This field is optional. If you do decide to use it though, both the internal ID AND the nickname will work.\n\n`editNicknameToCustomPalette(idOrNick, newName)` This function allows you to edit the nickname of your custom palette \nto something else. Both arguments require strings. You can use it's nickname you assigned it, or it's internal ID.\n\n`printFullPaletteList(path)` This function outputs a text file to the folder path outlining the palettes available, both\ndefault palettes and custom. It shows information such as their name, description, date created, color set, nickname,\nand more. The required argument is a string of a folder path, which must already exist. Here's an example of how to\nformat it: `C:\\Users\\Mark\\Desktop`\n\n`clearAllCustomPalettes()` This removes all custom palettes from your config. Please note that the default palettes \nwill not be removed.\n\n`removeCustomPalette(idOrNick)` This function removes the custom palette from your config. It takes a string argument\nof either it's internal ID, or a nickname you've previously given it.\n\n`removeCustomPaletteNickname(idOrNick)` This function strips any nickname associated with a custom palette. It takes a\nstring argument of either the internal ID or a previous nickname.\n\n`clearCustomPaletteNicknames()` This removes all nicknames from all custom palettes.\n\n### Partial Save Control\n\nOnce the first frame of a stream is read, a PartialSave object is created. This is essentially what manages the binary\nstrings, and holds various information on it's state. These functions help better interface with them.\n\n`updatePartialSave(streamSHA, reattemptAssembly = True, passwordUpdate = None, scryptN = None, scryptR = None, \nscryptP = None, changeOutputPath = None)` This function allows you to update various parameters of the save. Requires\na string input for `streamSHA` which is the ID number of the string. \n\nArgument `reattemptAssembly` makes the assembler\nattempt to reassemble the frames, as well as output the embedded files and folders. This would be used in the case of\nan incorrect password or scrypt parameters, and you'd like to try again.\n\nArgument `passwordUpdate` takes a string argument, and will add (or replace) the encryption key tied to this stream.\n\nArguments `scryptN, scryptR and scryptP` change the scrypt parameters used to derive the key used for decryption. If\nthe scrypt parameters were left at default during `write()` of the stream, these can be left as is. Otherwise, the \ncustom values will need to be inputted whether here or in the optional arguments of `read()`.\n\n`beginAssembly(streamSHA)` This function exists to initiate assembly of a package at a later time, rather than doing so \nimmediately for whatever reason.\n\n`printFullSaveList(path, debugData=False)` This function outputs a text file to the folder path outlining all (if any)\npartial saves you have on your system. You can check their status, as well as the state of the PartialSave object \nitself. Argument `debugData` is `False` by default, but enabling it to `True` outputs various debug information \npertaining to the object as well, which wouldn't serve much utility seeing for someone such as a casual end user.\n\n`removePartialSave(streamSHA)` Using a string argument for the stream's ID (or stream SHA, as commonly used), this will\nremove the object from your config, as well as remove all temporary data associated with it.\n\n`removeAllPartialSaves()` All saves are removed from the config object, clearing all temporary data.\n\n### General Configuration\n\n`outputStats(path)` This function gives you a neat bird's eye view of your BitGlitter usage. During all `read()` and \n`write()` cycles, the total amount of data transferred, as well as total frames transferred and individual blocks \nscanned gets added to a running total. Argument `path` requires a string argument of a folder path that already exists.\nA small text file will be written to this location.\n\n`clearStats()` All statistics will be reset to zero.\n\n`clearSession()` This wipes all inputted data. Custom colors, statistics, and partial save objects will all be erased,\nas well as any temporary data for the partially read streams. This is in essence a hard reset.\n\n# Contributing\n\nWhether you're a seasoned programmer or brand new, there's plenty of things you can do to help this project succeed.\nJoin our discord server, and check out all of the information I posted in the \"Information\" category. Thank you for\nyour interest!\n\n**Discord Link**\n\n**https://discord.gg/t9uv2pZ**\n\nAlso, be sure to check out the \n[contributing master page](https://github.com/MarkMichon1/BitGlitter/wiki/Contributing-Master-Page). It contains a lot\nof information.\n\n![Splitter](https://i.imgur.com/qIygifj.png)\n\n### Practical Limits\n\nIt's worth stating the constraints you may face while using this. While the images and video BitGlitter exports are\nlossless (no compression applied), the \"real world\" on the internet is much different. For instance, multimedia \nuploaded to popular social media sites is regularly compressed in order to save space (and ultimately cut down on \nexpenses). You are protected *to an extent* with BitGlitter from this. Write parameters are fully customizable\nprimarily for this reason. \n\nWhile you can have greater throughput with larger colorsets, smaller blocks, and faster framerates, there may be a\npractical limit to whether it will work depending on the degree their compression reduces quality. At the expense of\nthroughput, larger blocks, slower framerates, and fewer colors used will make the stream *far* more resistant to\npossible corruption. Approaching the extreme limits of these parameters (tiny block sizes, very fast framerates, very\nlarge colorsets), in terms of reading it and converting it back into data, requires very precise measurements of\nposition and color value; *a codec's purpose is to blur those precise values to reduce it's bitrate, and in turn it's\nfile size.* While BitGlitter will detect corruption and perform an \"emergency stop,\" I know you don't want to deal with\nthat, and neither do the people you're sharing with.\n\nIn closing, know the environment the video will be used in to ensure success in reading it.\n\n# MIT License\n\u00c2\u00a9 2019 - \u00e2\u02c6\u017e Mark Michon\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated \ndocumentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the \nrights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit\n persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the \nSoftware.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE \nWARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR \nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR \nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/MarkMichon1/BitGlitter", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "BitGlitter", "package_url": "https://pypi.org/project/BitGlitter/", "platform": "", "project_url": "https://pypi.org/project/BitGlitter/", "project_urls": { "Homepage": "https://github.com/MarkMichon1/BitGlitter" }, "release_url": "https://pypi.org/project/BitGlitter/1.0.2/", "requires_dist": null, "requires_python": "", "summary": "\u26a1 Embed data payloads inside of ordinary images or video, through high performance 2-D matrix codes.", "version": "1.0.2" }, "last_serial": 5368543, "releases": { "1.0.1": [ { "comment_text": "", "digests": { "md5": "5e87e8e90c82f0fcd5bf72f725d6a3d9", "sha256": "c5b127e20889513cea906b420f45f900c7c9e4ecf870f63aaf4b95747ec333ee" }, "downloads": -1, "filename": "BitGlitter-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "5e87e8e90c82f0fcd5bf72f725d6a3d9", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 12748612, "upload_time": "2019-05-24T23:25:45", "url": "https://files.pythonhosted.org/packages/97/a2/c89c1498b106af58489039aa0d829c02a15138fe019315c0e7cb20bd4b2e/BitGlitter-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a939d1d930a1b88a70b992d3518021db", "sha256": "19613812e5143728807ae8c8be9a36e25dbc44b2c8ce6707647367d379545506" }, "downloads": -1, "filename": "BitGlitter-1.0.1.tar.gz", "has_sig": false, "md5_digest": "a939d1d930a1b88a70b992d3518021db", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 5380593, "upload_time": "2019-05-24T23:25:56", "url": "https://files.pythonhosted.org/packages/56/6e/c5ea3c02e1c57e116a3686736f5814b2611e019427d080d97ea0a2f6d517/BitGlitter-1.0.1.tar.gz" } ], "1.0.2": [ { "comment_text": "", "digests": { "md5": "c18ebef5750e3f5584a2fdd012c29f9b", "sha256": "bdcc28b9614756339230245f44010150250b32e5272d4bd21f1d78b70d303330" }, "downloads": -1, "filename": "BitGlitter-1.0.2.tar.gz", "has_sig": false, "md5_digest": "c18ebef5750e3f5584a2fdd012c29f9b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2879233, "upload_time": "2019-06-06T18:45:33", "url": "https://files.pythonhosted.org/packages/0b/c4/8044bd40c777f15f843dad9994b54a6598f6e45aec0c79ac9fecae994416/BitGlitter-1.0.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c18ebef5750e3f5584a2fdd012c29f9b", "sha256": "bdcc28b9614756339230245f44010150250b32e5272d4bd21f1d78b70d303330" }, "downloads": -1, "filename": "BitGlitter-1.0.2.tar.gz", "has_sig": false, "md5_digest": "c18ebef5750e3f5584a2fdd012c29f9b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2879233, "upload_time": "2019-06-06T18:45:33", "url": "https://files.pythonhosted.org/packages/0b/c4/8044bd40c777f15f843dad9994b54a6598f6e45aec0c79ac9fecae994416/BitGlitter-1.0.2.tar.gz" } ] }