blob8108

I've finally released Kurt 2.0 after several months of coding.

Kurt's a Python library for working with Scratch project files.

The new version features:
* An all-new interface
* Support for both Scratch 1.4 and 2.0
* API documentation hosted on Read the Docs

It's extensible to support new file formats (for Scratch mods), and I'm working with Hardmath123 to integrate Snapin8r so that Kurt has support for Snap! as well.

If you're a Python coder: try it out, make awesome stuff, and post about it here!

If you're not: Kurt also includes a parser for converting plain text into Scratch blocks, which you can try out at kurt.herokuapp.com without needing to install anything! (It's also currently the only way to convert Scratch 2.0 back to 1.4.)

The slides from my talk about Kurt at the Barcelona Scratch conference are on Scratch.

Enjoy!

OS-specific Install instructions
Windows
You need Python 2.7.
* Install pillow: http://www.lfd.uci.edu/~gohlke/pythonlibs/#pillow
* Install setuptools: http://www.lfd.uci.edu/~gohlke/pythonlibs/#setuptools
* Install pip: http://www.lfd.uci.edu/~gohlke/pythonlibs/#pip
* Open a Command Prompt (Start->Run, “cmd”), and type `cd C:\Python27\Scripts` (or wherever you installed Python)
* Type `pip install kurt`

Mac OS X
OS X comes with Python.
* Install pip. Type into a Terminal window:
$ curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py
$ sudo python get-pip.py
* Install pillow by following these instructions
* Then run:
$ sudo pip install kurt

Ubuntu / Raspbian
Open a Terminal window, and type:
$ sudo apt-get install python-dev python-pip python-imaging
$ sudo pip install kurt
You may need to run this first:
sudo apt-get install libfreetype6-dev

jji7skyline

This looks great I'm looking forward to testing it out

EDIT: installing on OSX 10.6.8 using the PyPI method (easy_install produces same errors) I get these warnings and the install fails.
Downloading https://pypi.python.org/packages/source/P/Pillow/Pillow-2.1.0.zip#md5=ec630d8ae15d4a3c4ae7b7efdeac8200
Processing Pillow-2.1.0.zip
Running Pillow-2.1.0/setup.py -q bdist_egg --dist-dir /var/folders/MH/MHjZJofgEwKgDMVYOvY61E+++TI/-Tmp-/easy_install-_2ii1E/Pillow-2.1.0/egg-dist-tmp-4OqQKu
warning: no previously-included files found matching '.hgignore'
warning: no previously-included files found matching '.hgtags'
warning: no previously-included files found matching 'BUILDME.bat'
warning: no previously-included files found matching 'make-manifest.py'
warning: no previously-included files found matching 'SHIP'
warning: no previously-included files found matching 'SHIP.bat'
warning: no files found matching 'COPYING'
warning: no files found matching '*.html' under directory 'docs'
warning: no files found matching '*.css' under directory 'docs'
warning: no files found matching 'README' under directory 'docs'
warning: no files found matching 'CHANGES' under directory 'docs'
warning: no files found matching 'CONTENTS' under directory 'docs'
--- using frameworks at /System/Library/Frameworks
/usr/libexec/gcc/powerpc-apple-darwin10/4.2.1/as: assembler (/usr/bin/../libexec/gcc/darwin/ppc/as or /usr/bin/../local/libexec/gcc/darwin/ppc/as) for architecture ppc not installed
Installed assemblers are:
/usr/bin/../libexec/gcc/darwin/x86_64/as for architecture x86_64
/usr/bin/../libexec/gcc/darwin/i386/as for architecture i386
_imaging.c:3477: fatal error: error writing to -: Broken pipe
compilation terminated.
lipo: can't open input file: /var/folders/MH/MHjZJofgEwKgDMVYOvY61E+++TI/-Tmp-//ccLvXJ94.out (No such file or directory)
error: Setup script exited with error: command 'gcc-4.2' failed with exit status 1

blob8108

@jj7skyline Thanks! You may have trouble installing binary packages like pillow using pip if you don't have XCode etc installed. Try looking for an installer.

EDIT: Ah, my bad. Apparently there aren't any Pillow binaries for OS X. Try following the install instructions here.

davidkt

Why do we need Kurt 2.0? It's simple:
import zipfile, json
proj = zipfile.ZipFile("test.sb2")
proj.extractall("temp/test")
print json.loads("temp/test/project.json")
That's for reading project files; for writing:
import zipfile, json
proj = zipfile.ZipFile("test.sb2")
proj.extractall("temp/test")
data = raw_input("New project data: ")
json.dumps(data, "temp/test/project.json")
proj.write("temp/test/project.json"
That's all there is to it!

Hardmath123

Kurt lets you do all sorts of neat stuff between various formats. Like you can turn a scratch 2.0 project into a scratch 1.4 project!

blob8108

davidkt wrote:

Why do we need Kurt 2.0? It's simple:
Good question!

Sure, it's easy to read the ZIP file – that's what's so great about the 2.0 format – but it's a pain to actually manipulate the JSON. Compare the following code to import images into a project file.

With Kurt:
import kurt
import sys
p = kurt.Project()
for path in sys.argv[1:-1]:
    costume = kurt.Costume.load(path)
    p.stage.costumes.append(costume)
p.save(sys.argv[-1])

Without Kurt:
import hashlib
import json
import os
import sys
import time
import zipfile
import PIL
project = {
    'children': [],
    'costumes': [],
    'currentCostumeIndex': 0,
    'info': {
        'author': u'',
        'comment': u'',
        'flashVersion': '',
        'hasCloudData': False,
        'projectID': '',
        'scriptCount': 0,
        'spriteCount': 0,
        'userAgent': '',
        'videoOn': False
    },
    'lists': [],
    'objName': 'Stage',
    'penLayerMD5': '279467d0d49e152706ed66539b577c00.png',
    'scriptComments': [],
    'scripts': [],
    'sounds': [],
    'tempoBPM': 60,
    'variables': [],
    'videoAlpha': 0.5,
}
zip_file = zipfile.ZipFile(sys.argv[-1], "w")
for (i, path) in enumerate(sys.argv[1:-1]):
    contents = open(path, 'rb').read()
    (folder, filename) = os.path.split(path)
    (name, extension) = os.path.splitext(filename)
    assert extension.lower() in (".png", ".jpg")
    zi = zipfile.ZipInfo("%i.png" % i)
    zi.date_time = time.localtime(time.time())[:6]
    zi.compress_type = zipfile.ZIP_DEFLATED
    zi.external_attr = 0777 << 16L
    zip_file.writestr(zi, contents)
    project['costumes'].append({
        'baseLayerID': i,
        'baseLayerMD5': hashlib.md5(contents).hexdigest() + extension,
        'bitmapResolution': 1,
        'costumeName': u'image1',
        'rotationCenterX': 240,
        'rotationCenterY': 180,
    })
zi = zipfile.ZipInfo("project.json")
zi.date_time = time.localtime(time.time())[:6]
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = 0777 << 16L
zip_file.writestr(zi, json.dumps(project))

—and this is a simple example. Kurt provides a nice interface for working with the data in the Scratch files, and it's the same interface whether you're using Scratch 1.4 or Scratch 2.0; so code you write for working with Scratch 1.4 files will also work with Scratch 2.0 files, with no extra effort on your part. Which also means you can convert between formats very easily, as Hardmath points out.

It also makes working with blocks much easier: it has all the information for the blocks, can search them by text or command, and can even parse them from a special text syntax.

Does that answer your question?

nXIII

Most of the keys in info are optional, but Kurt outputs invalid values like empty strings for the user agent/project ID fields. It might be better not to include them in kurt-generated files at all.

davidkt

blob8108 wrote:

davidkt wrote:

Why do we need Kurt 2.0? It's simple:
Good question!

Sure, it's easy to read the ZIP file – that's what's so great about the 2.0 format – but it's a pain to actually manipulate the JSON. Compare the following code to import images into a project file.

With Kurt:
import kurt
import sys
p = kurt.Project()
for path in sys.argv[1:-1]:
    costume = kurt.Costume.load(path)
    p.stage.costumes.append(costume)
p.save(sys.argv[-1])

Without Kurt:
import hashlib
import json
import os
import sys
import time
import zipfile
import PIL
project = {
    'children': [],
    'costumes': [],
    'currentCostumeIndex': 0,
    'info': {
        'author': u'',
        'comment': u'',
        'flashVersion': '',
        'hasCloudData': False,
        'projectID': '',
        'scriptCount': 0,
        'spriteCount': 0,
        'userAgent': '',
        'videoOn': False
    },
    'lists': [],
    'objName': 'Stage',
    'penLayerMD5': '279467d0d49e152706ed66539b577c00.png',
    'scriptComments': [],
    'scripts': [],
    'sounds': [],
    'tempoBPM': 60,
    'variables': [],
    'videoAlpha': 0.5,
}
zip_file = zipfile.ZipFile(sys.argv[-1], "w")
for (i, path) in enumerate(sys.argv[1:-1]):
    contents = open(path, 'rb').read()
    (folder, filename) = os.path.split(path)
    (name, extension) = os.path.splitext(filename)
    assert extension.lower() in (".png", ".jpg")
    zi = zipfile.ZipInfo("%i.png" % i)
    zi.date_time = time.localtime(time.time())[:6]
    zi.compress_type = zipfile.ZIP_DEFLATED
    zi.external_attr = 0777 << 16L
    zip_file.writestr(zi, contents)
    project['costumes'].append({
        'baseLayerID': i,
        'baseLayerMD5': hashlib.md5(contents).hexdigest() + extension,
        'bitmapResolution': 1,
        'costumeName': u'image1',
        'rotationCenterX': 240,
        'rotationCenterY': 180,
    })
zi = zipfile.ZipInfo("project.json")
zi.date_time = time.localtime(time.time())[:6]
zi.compress_type = zipfile.ZIP_DEFLATED
zi.external_attr = 0777 << 16L
zip_file.writestr(zi, json.dumps(project))

—and this is a simple example. Kurt provides a nice interface for working with the data in the Scratch files, and it's the same interface whether you're using Scratch 1.4 or Scratch 2.0; so code you write for working with Scratch 1.4 files will also work with Scratch 2.0 files, with no extra effort on your part. Which also means you can convert between formats very easily, as Hardmath points out.

It also makes working with blocks much easier: it has all the information for the blocks, can search them by text or command, and can even parse them from a special text syntax.

Does that answer your question?
Yeah. It just must have been a lot easier to make Kurt 2.0, as I can't even see how you made kurt 1.4.
And what are construct and pillow for? (Construct is really a pain- when I tried importing kurt it said I need construct (well the error message said that) and I installed construct and it said I need six. Why can't it just install everything at once?)

blob8108

davidkt wrote:

It just must have been a lot easier to make Kurt 2.0, as I can't even see how you made kurt 1.4.
The 2.0-specific bits were much easier, yes. But Kurt 2.0 overall was much harder, as it supports both and I put a lot of thought into the interface.

Construct makes it much easier to process binary data, like the Scratch 1.4 file format. Pillow is the Python Imaging Library, used for loading / converting / saving images.

Why can't it just install everything at once?
If you use `pip install kurt`, it should fetch and install all the dependencies for you. But you may have trouble with pillow because you need binaries or to compile it from source.

blob8108

nXIII wrote:

Most of the keys in info are optional, but Kurt outputs invalid values like empty strings for the user agent/project ID fields. It might be better not to include them in kurt-generated files at all.
Mmkay. Does it matter outputting empty ones? Which ones precisely should it not include? After all, your validator accepts it…

davidkt

blob8108 wrote:

davidkt wrote:

It just must have been a lot easier to make Kurt 2.0, as I can't even see how you made kurt 1.4.
The 2.0-specific bits were much easier, yes. But Kurt 2.0 overall was much harder, as it supports both and I put a lot of thought into the interface.
I had no idea it supported both…
Pillow is the Python Imaging Library, used for loading / converting / saving images.
That's PIL.
Why can't it just install everything at once?
If you use `pip install kurt`, it should fetch and install all the dependencies for you. But you may have trouble with pillow because you need binaries or to compile it from source.
I already have PIL…

blob8108

davidkt wrote:

blob8108 wrote:

Pillow is the Python Imaging Library, used for loading / converting / saving images.
That's PIL.
Mmm. Pillow is the friendly PIL fork.

davidkt

blob8108 wrote:

davidkt wrote:

blob8108 wrote:

Pillow is the Python Imaging Library, used for loading / converting / saving images.
That's PIL.
Mmm. Pillow is the friendly PIL fork.
So can I use PIL for kurt?

blob8108

In theory. pip might complain.

davidkt

pip?

nXIII

blob8108 wrote:

nXIII wrote:

Most of the keys in info are optional, but Kurt outputs invalid values like empty strings for the user agent/project ID fields. It might be better not to include them in kurt-generated files at all.
Mmkay. Does it matter outputting empty ones? Which ones precisely should it not include? After all, your validator accepts it…
It's technically valid; I just think it makes more sense not to include them when you don't have real values for them. The validator isn't too picky about what you shove in info fields.

blob8108

(Just reminding myself to update the first post…)

Travellingcritic

Thanks! This helped me download the pymunk library as well, which I was having troubles downloading.

blob8108

Travellingcritic wrote:

This helped me download the pymunk library as well
Yeah, installing Python packages is still stupidly hard.

Macie1234

On raspian terminal I ran sudo apt-get install python-pip then pip install m30w.