diff --git a/ruby/bin/jamtrack.rb b/ruby/bin/jamtrack.rb new file mode 100644 index 000000000..2152352db --- /dev/null +++ b/ruby/bin/jamtrack.rb @@ -0,0 +1 @@ +{"SKU": "0044454545454", "METADATA_VER": 1, "TrackData": {"track_2": {"length": 0.1, "bitrate": 96000}, "track_1": {"length": 264.5681632653061, "bitrate": 128003}, "track_0": {"length": 405.054693877551, "bitrate": 128003}}, "TrackInstrument": {"track_1": "Alto Sax", "track_0": "Bass Guitar"}, "Title": "SmFtS2F6YW0gcm9sbGluZyBpbiB0aGUgZG91Z2g="} \ No newline at end of file diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 07234042f..b3b1a3ee1 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -74,6 +74,7 @@ require "jam_ruby/app/uploaders/mix_uploader" require "jam_ruby/app/uploaders/music_notation_uploader" require "jam_ruby/app/uploaders/jam_track_uploader" require "jam_ruby/app/uploaders/jam_track_track_uploader" +require "jam_ruby/app/uploaders/jam_track_right_uploader" require "jam_ruby/app/uploaders/max_mind_release_uploader" require "jam_ruby/lib/desk_multipass" require "jam_ruby/lib/ip" @@ -189,6 +190,7 @@ require "jam_ruby/models/jam_company" require "jam_ruby/models/user_sync" require "jam_ruby/models/video_source" require "jam_ruby/models/recorded_video" +require "jam_ruby/jam_tracks_manager" include Jampb diff --git a/ruby/lib/jam_ruby/app/uploaders/jam_track_right_uploader.rb b/ruby/lib/jam_ruby/app/uploaders/jam_track_right_uploader.rb new file mode 100644 index 000000000..e33494063 --- /dev/null +++ b/ruby/lib/jam_ruby/app/uploaders/jam_track_right_uploader.rb @@ -0,0 +1,28 @@ +class JamTrackRightUploader < CarrierWave::Uploader::Base + # include CarrierWaveDirect::Uploader + include CarrierWave::MimeTypes + + process :set_content_type + + def initialize(*) + super + JamRuby::UploaderConfiguration.set_aws_private_configuration(self) + end + + # Add a white list of extensions which are allowed to be uploaded. + def extension_white_list + %w(jkz) + end + + def store_dir + nil + end + + def md5 + @md5 ||= ::Digest::MD5.file(current_path).hexdigest + end + + def filename + "#{model.store_dir}/#{model.filename}" if model.id + end +end diff --git a/ruby/lib/jam_ruby/jam_tracks_manager.rb b/ruby/lib/jam_ruby/jam_tracks_manager.rb new file mode 100644 index 000000000..07b264df0 --- /dev/null +++ b/ruby/lib/jam_ruby/jam_tracks_manager.rb @@ -0,0 +1,22 @@ +module JamRuby + + # describes an audio track (like the drums, or guitar) that comprises a JamTrack + class JamTracksManager + class << self + def save_jam_track(jam_track, user) + spec = Gem::Specification.find_by_name("jam_ruby") + py_root = spec.gem_dir + "/lib/py/jam_tracks/" + puts "Executing python in #{py_root}" + + sku=""#jam_track.sku + public_key="" + private_key="" + tracks_filename="" + output_jkz="" + title="" + `python #{py_root}jkcreate.py -D -k #{sku} -c art.png -p #{public_key} -s #{private_key} -I #{tracks_filename} -o #{output_jkz} -t '#{title}'` + end + end + end + +end diff --git a/ruby/lib/jam_ruby/models/jam_track_right.rb b/ruby/lib/jam_ruby/models/jam_track_right.rb index 57c0a444f..33a036a05 100644 --- a/ruby/lib/jam_ruby/models/jam_track_right.rb +++ b/ruby/lib/jam_ruby/models/jam_track_right.rb @@ -2,7 +2,7 @@ module JamRuby # describes what users have rights to which tracks class JamTrackRight < ActiveRecord::Base - attr_accessible :user, :jam_track, :user_id, :jam_track_id + attr_accessible :user, :jam_track, :user_id, :jam_track_id, :url, :md5, :length, :download_count belongs_to :user, class_name: "JamRuby::User" # the owner, or purchaser of the jam_track belongs_to :jam_track, class_name: "JamRuby::JamTrack" @@ -11,6 +11,10 @@ module JamRuby validate :verify_download_count validates_uniqueness_of :user_id, scope: :jam_track_id + + # Uploads the JKZ: + mount_uploader :url, JamTrackRightUploader + MAX_JAM_TRACK_DOWNLOADS = 1000 def verify_download_count @@ -18,5 +22,11 @@ module JamRuby errors.add(:download_count, "must be less than or equal to #{MAX_JAM_TRACK_DOWNLOADS}") end end + + # Create user-specific JKZ for the associated jam_track: + def create_jkz + + end + end end diff --git a/ruby/lib/py/jam_tracks/jamtrack.info b/ruby/lib/py/jam_tracks/jamtrack.info new file mode 100644 index 000000000..2152352db --- /dev/null +++ b/ruby/lib/py/jam_tracks/jamtrack.info @@ -0,0 +1 @@ +{"SKU": "0044454545454", "METADATA_VER": 1, "TrackData": {"track_2": {"length": 0.1, "bitrate": 96000}, "track_1": {"length": 264.5681632653061, "bitrate": 128003}, "track_0": {"length": 405.054693877551, "bitrate": 128003}}, "TrackInstrument": {"track_1": "Alto Sax", "track_0": "Bass Guitar"}, "Title": "SmFtS2F6YW0gcm9sbGluZyBpbiB0aGUgZG91Z2g="} \ No newline at end of file diff --git a/ruby/lib/py/jam_tracks/jamtracklist.txt b/ruby/lib/py/jam_tracks/jamtracklist.txt new file mode 100644 index 000000000..6fa0a517d --- /dev/null +++ b/ruby/lib/py/jam_tracks/jamtracklist.txt @@ -0,0 +1,8 @@ +You Shook Me All Night Long/You Shook Me All Night Long Stem - Bass.ogg+Bass +You Shook Me All Night Long/You Shook Me All Night Long Stem - Drums.ogg+Drums +You Shook Me All Night Long/You Shook Me All Night Long Stem - Guitar - Main.ogg+Guitar Main +You Shook Me All Night Long/You Shook Me All Night Long Stem - Guitar - Rhythm 2.ogg+Guitar Rhythm 2 +You Shook Me All Night Long/You Shook Me All Night Long Stem - Guitar - Rhythm.ogg+Guitar Rhythm +You Shook Me All Night Long/You Shook Me All Night Long Stem - Guitar - Solo.ogg+Guitar Solo +You Shook Me All Night Long/You Shook Me All Night Long Stem - Vocals - Background.ogg+Vocals Background +You Shook Me All Night Long/You Shook Me All Night Long Stem - Vocals - Lead.ogg+Vocals Lead diff --git a/ruby/lib/py/jam_tracks/jkaes.py b/ruby/lib/py/jam_tracks/jkaes.py new file mode 100755 index 000000000..49966001f --- /dev/null +++ b/ruby/lib/py/jam_tracks/jkaes.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python + +from Crypto.Cipher import AES +from Crypto import Random +import base64 +import os +import binascii +import json +import sys + +class JKaes: + + # the key size for the cipher object; must be 16, 24, or 32 for AES + KEY_SIZE = 16 + #always 16 bytes for aes + BLOCK_SIZE = 16 + + # the character used for padding--with a block cipher such as AES, the value + # you encrypt must be a multiple of BLOCK_SIZE in length. This character is + # used to ensure that your value is always a multiple of BLOCK_SIZE + #PADDING_CHARACTER = '{' + PADDING_CHARACTER = b'0' + PADDING = 0 + op = 'encode' + musicFile = '' + + KEY='' + IV='' + + CIPHER_MODE = AES.MODE_CBC + # CIPHER_MODE = AES.MODE_CFB + + def __init__(self, fileName, keyFile='',operation='encode'): + self.op = operation + self.musicFile = fileName + if len(keyFile) != 0: + self.initKeyFromFile(keyFile) + else: + if operation == 'encode': + self.generateNewKeys() + + # one-liner to sufficiently pad the text to be encrypted + def updatePadSize(self,dlen): + self.PADDING = (self.BLOCK_SIZE - dlen % self.BLOCK_SIZE) + + def getPadding(self): + return self.PADDING + + def pad(self,s): + self.updatePadSize(len(s)) + pdata = self.PADDING * self.PADDING_CHARACTER + if len(pdata) != self.PADDING : + sys.exit('Pad data length %d does match %d. Check python pad character size!' % + (len(pdata), self.PADDING)) + s = s + pdata + return s + + def EncodeAEStoB64(self,c, s): + return base64.b64encode(c.encrypt(self.pad(s))) + + def b64DecodeAES(self,c, e): + return c.decrypt(base64.b64decode(e)).rstrip(self.PADDING_CHARACTER) + + + def initKeyFromFile(self,fname): + (self.KEY,self.IV)= self.readAesKeyFile(fname) + + def generateNewKeys(self): + self.KEY = self.generateAesKey(); + self.IV = self.generateAesIv() + + def generateAesKey(self,keySize=KEY_SIZE): + # generate a random secret key + #secret = Random.get_random_bytes(keySize) + secret = Random.new().read(keySize) + return secret + + def generateAesIv(self): + secret = Random.new().read(AES.block_size) + return secret + + def getKeyStr(self): + str = '%s_%s_%s' % (len(self.KEY),base64.b64encode(self.IV),base64.b64encode(self.KEY)) + #print 'okey=',str + return str + def getKeyModeStr(self): + if self.CIPHER_MODE == AES.MODE_CBC: + return 'CBC' + elif self.CIPHER_MODE == AES.MODE_CFB: + return 'CFB' + else: + sys.exit('Bad AES key mode') + + def setKey(self, content): + (keySize, ivstr, keystr) = content.split('_') + #print 'ikey=',content + self.IV = base64.b64decode(ivstr) + self.KEY = base64.b64decode(keystr) + + def writeAesKeytoFile(self, iv,key,Keyfilename): + #initialization vector - iv + #key - must be the same size as the iv + #output filename + str = '%s_%s_%s' % (len(key),base64.b64encode(iv),base64.b64encode(key)) + #print 'okey=',str + fo = open(Keyfilename, 'w') + fo.write(str) + fo.close() + + def writeAesKeytoFile(self,Keyfilename): + self.writeAesKeytoFile(self.IV,self.KEY,Keyfilename) + + def readAesKeyFile(self,keyFilename): + # Given the filename of a file that contains a public or private key, + # return the key as a (n,e) or (n,d) tuple value. + fo = open(keyFilename) + content = fo.read() + fo.close() + (keySize, ivstr, keystr) = content.split('_') + + #print 'ikey=',content + iv = base64.b64decode(ivstr) + key = base64.b64decode(keystr) + return (int(keySize), iv, key) + + + def WriteFileData(self,outfile,data): + #ddata = base64.b64decode(data) + fi = open(outfile,'wb') + fi.write(data); + + def LoadFileData(self, infile): + fi = open(infile,'rb') + data = fi.read(); + return data + #edata = base64.b64encode(data) + #return edata + + + def WriteAesFileData(self, outfile,data): + # These files are assumed to be in cypher text - so we won't b64 encode/decode them + fo = open(outfile,'wb') + fo.write(data) + + def LoadAesFileData(self, infile): + # These files are assumed to be in cypher text - so we won't b64 encode/decode them + fi = open(infile,'rb') + data = fi.read(); + return data + + def EncodeFileToMsgWithKey(self, key, iv, infile): + infileData = self.LoadFileData(infile) + pdata = self.pad(infileData) + cipher = AES.new(key, self.CIPHER_MODE, iv) + msg = cipher.encrypt(pdata) + return msg + + + def EncodeFileWithKeyAndWrite(self, key, iv, infile,outFile): + msg = self.EncodeFileToMsgWithKey(key, iv, infile) + self.WriteAesFileData(outFile,msg) + return msg + + def EncodeFileToMsg(self, infile): + infileData = self.LoadFileData(infile) + # file data must be in plain text + pdata = self.pad(infileData) + cipher = AES.new(self.KEY, self.CIPHER_MODE, self.IV) + msg = cipher.encrypt(pdata) + return msg + + + def EncodeFileAndWrite(self, infile,outFile): + edata = self.EncodeFileToMsg(infile) + self.WriteAesFileData(outFile,edata) + #return edata + + def DecodeFileToMsgWithKey(self,key, iv, infile): + infileData = self.LoadAesFileData(infile) + cipher = AES.new(key, self.CIPHER_MODE, iv) + #expects the file to have the right number of blocks + data = cipher.decrypt(infileData) + return data + + def DecodeFileWithKeyAndWrite(self,key, iv, infile,outFile): + msg = self.DecodeFileToMsgWithKey(key, iv, infile) + self.WriteFileData(outFile,msg) + + def DecodeFileToMsg(self, infile): + msg = self.DecodeFileToMsgWithKey(self.KEY, self.IV, infile) + return msg + + def DecodeFileAndWrite(self, infile,outFile): + data = self.DecodeFileToMsg(infile) + self.WriteFileData(outFile,data) + #return data + + def DecodeMsg(self, pdata): + cipher = AES.new(self.KEY,self.CIPHER_MODE, self.IV) + data = cipher.decrypt(pdata) + return data + + def DecodeU64Msg(self, pdata): + cipher = AES.new(self.KEY, self.CIPHER_MODE, self.IV) + data = self.b64DecodeAES(cipher, pdata) + return data + + + + + + diff --git a/ruby/lib/py/jam_tracks/jkaes.pyc b/ruby/lib/py/jam_tracks/jkaes.pyc new file mode 100644 index 000000000..ea98827c8 Binary files /dev/null and b/ruby/lib/py/jam_tracks/jkaes.pyc differ diff --git a/ruby/lib/py/jam_tracks/jkasset.py b/ruby/lib/py/jam_tracks/jkasset.py new file mode 100755 index 000000000..08af7ddb1 --- /dev/null +++ b/ruby/lib/py/jam_tracks/jkasset.py @@ -0,0 +1,431 @@ +import json +from jktrack import JKtrack +from jkrsa import JKrsa +from pprint import pprint +import sys +import os +import zipfile +import random +import base64 +import tempfile +import datetime +import zipfile +import filecmp + +#debug flags +DEBUG_ENABLE = 0 +COMPARE_PARSEFILE = False + + +JAMTRACK_PACKAGE_VERSION = 1 +JAMTRACK_PACKAGE_INFO_FILE = 'jamtrack.info' +JAMTRACK_MEDIA_META_FILE = 'media_meta.info' +JAMTRACK_COVER_ART_TAG ='cover_art' + +def print_info(archive_name): + zf = zipfile.ZipFile(archive_name) + for info in zf.infolist(): + print info.filename + print '\tComment:\t', info.comment + print '\tModified:\t', datetime.datetime(*info.date_time) + print '\tSystem:\t\t', info.create_system, '(0 = Windows, 3 = Unix)' + print '\tZIP version:\t', info.create_version + print '\tCompressed:\t', info.compress_size, 'bytes' + print '\tUncompressed:\t', info.file_size, 'bytes' + print + +import zipfile +try: + import zlib + compression = zipfile.ZIP_DEFLATED +except: + compression = zipfile.ZIP_STORED + +modes = { zipfile.ZIP_DEFLATED: 'deflated', + zipfile.ZIP_STORED: 'stored', + } +#use store - the encrypted files really does not gain from any zlib attempt to encrypt +compression = zipfile.ZIP_STORED + +class JKasset: + rawMediaFiles=[] + encryptedMediaFiles = [] + mediaAesKeyFile = [] + packageFileInfo = '' + otherAssetFile = '' + coverArtFile='' + encryptedCoverFile ='' + rsaPubKeyFile='' + rsaPrivKeyFile='' + outputPackageFile='' + packageFileName='' + def __init__(self, fileName, operation='encode', pubRsaKeyFile=''): + self.op = operation + self.packageFileInfo = fileName + if operation == 'encode': + self.loadEncodeContainerData() + self.generateContainer() + if operation == 'decode': + # print fileName + #self.loadDecodeContainerData() + self.parseContainer(pubRsaKeyFile) + + def loadEncodeContainerData(self): + json_data=open(self.packageFileInfo) + jdata = json.load(json_data) + print + pprint(jdata) + json_data.close() + + self.rawMediaFiles = jdata['tracks'] + + if len(self.rawMediaFiles) == 0: + sys.exit('ERROR: No tracks specified.') + + try: + self.mediaAesKeyFile = jdata['aesKeyFiles'] + except: + print "no aes key file " + + self.otherAssetFile = jdata["jamktrack_info"] + if len(self.otherAssetFile) == 0: + sys.exit('ERROR: No JamTrack infoFile specified.') + + self.rsaPrivKeyFile = jdata['rsa_priv_file'] + self.rsaPubKeyFile = jdata["rsa_pub_file"] + + if len(self.rsaPubKeyFile) == 0: + sys.exit('ERROR: A RSA pubkey file must be specified.') + + self.outputPackageFile = jdata['container_file'] + if len(self.outputPackageFile) == 0: + sys.exit('ERROR: A output container file must be specified.') + + try: + self.coverArtFile = jdata['coverart'] + except: + print 'no covert art file' + + def encodeMediaFiles(self): + + for mediaFileInfo in self.rawMediaFiles: + print 'mediaFileInfo:',mediaFileInfo + ##the file, and track name + mediaFile = JKtrack(mediaFileInfo["name"],mediaFileInfo["trackName"]) + if not self.mediaAesKeyFile : + mediaFile.EncodeToTmpFile(); + else: + mediaFile.setKey(self.mediaAesKeyFile) + mediaFile.EncodeToTmpFile(); + + self.encryptedMediaFiles.append(mediaFile) + + # Debug: save a copy of the encoded file + # mediaFile.saveEncFileCopy('enc_'+mediaFileInfo["name"]) + + def encodeCoverArt(self): + if not self.coverArtFile: + return + + coverFile = JKtrack(self.coverArtFile,JAMTRACK_COVER_ART_TAG) + if not self.mediaAesKeyFile : + coverFile.EncodeToTmpFile(); + else: + coverFile.setKey(self.mediaAesKeyFile) + coverFile.EncodeToTmpFile(); + + self.encryptedCoverFile = coverFile + + + def generateContainerCoverMetaData(self): + # create a json object with info on all the asset that are in the + # the container + if not self.encryptedCoverFile: + return '[{}]' + + mediaInfo = [] + trackNum=0 + mediaFileInfo = self.encryptedCoverFile + if mediaFileInfo: + statinfo = os.stat(mediaFileInfo.getOutputFileName()) + + info = { 'filename': mediaFileInfo.getCoverEncryptFilePackageName(), + 'datafile': mediaFileInfo.getCoverEncryptFilePackageName(trackNum), + 'padding': mediaFileInfo.getEncryptPadding(), + 'size': statinfo.st_size, + 'keymode': mediaFileInfo.getKeyModeStr(), + 'key': mediaFileInfo.getKeyStr()} + + mediaInfo.append(info) + return mediaInfo; + + def generateContainerMediaMetaData(self): + # create a json object with info on all the asset that are in the + # the container + mediaInfo = [] + trackNum=0 + for mediaFileInfo in self.encryptedMediaFiles: + statinfo = os.stat(mediaFileInfo.getOutputFileName()) + + info = { 'filename': mediaFileInfo.getEncryptFilePackageName(), + 'trackname': mediaFileInfo.getEncryptFilePackageName(trackNum), + 'padding': mediaFileInfo.getEncryptPadding(), + 'size': statinfo.st_size, + 'keymode': mediaFileInfo.getKeyModeStr(), + 'key': mediaFileInfo.getKeyStr()} + trackNum += 1 + mediaInfo.append(info) + return mediaInfo; + + def addJamTrackAssetToContainer(self,assetfile,rsa,zfile): + #this is assumed to be text file.. + fi = open(assetfile,'rb') + fi.seek(0) + amsg = fi.read(); + pstr = rsa.pad(amsg) + + #print pstr + + if DEBUG_ENABLE != 0: + print 'plaintext:', pstr + + encMediaStr = rsa.encryptMesssage(pstr) + edata = ''.join(encMediaStr) + metaInfoFile = tempfile.NamedTemporaryFile() + metaInfoFile.write(edata) + metaInfoFile.seek(0) + + if DEBUG_ENABLE != 0: + ddata = rsa.decryptMesssage(bytes(edata)) + print 'decrypt: ', ''.join(ddata) + + + try: + zfile.write(metaInfoFile.name, + arcname=JAMTRACK_PACKAGE_INFO_FILE, compress_type=compression) + except: + sys.exit("error writing package info file") + + + def initContainer(self): + + + mediaMeta = {'JPACK_VER': JAMTRACK_PACKAGE_VERSION, + 'jamktrack_info' : self.otherAssetFile, + JAMTRACK_COVER_ART_TAG : self.generateContainerCoverMetaData(), + "tracks": self.generateContainerMediaMetaData()} + + mediaMeta_str = json.dumps( mediaMeta ) + if DEBUG_ENABLE != 0: + pprint(mediaMeta) + #print mediaMeta_str + + #now rsa encrypt this string + rsa = JKrsa() + rsa.generateKey(self.rsaPrivKeyFile, self.rsaPubKeyFile) + mediaMeta_str = rsa.pad(mediaMeta_str) + + if DEBUG_ENABLE != 0: + print 'plaintext:', mediaMeta_str + + encMediaStr = rsa.encryptMesssage(mediaMeta_str) + edata = ''.join(encMediaStr) + + #write the encrypted media info to a file so we can zip in the asset + metaInfoFile = tempfile.NamedTemporaryFile() + metaInfoFile.write(edata) + metaInfoFile.seek(0) + + #try decrypting the data + if DEBUG_ENABLE != 0: + ddata = rsa.decryptMesssage(bytes(edata)) + print 'decrypt: ', ''.join(ddata) + + #zip package + if DEBUG_ENABLE != 0: + print 'creating archive' + #self.packageFileName = outfile = "media_%s" % (self.outputPackageFile) + self.packageFileName = outfile = "%s" % (self.outputPackageFile) + zfile = zipfile.ZipFile(outfile, mode='w') + + try: + if DEBUG_ENABLE != 0: + print 'adding media_meta file with compression mode', modes[compression] + zfile.write(metaInfoFile.name, + arcname=JAMTRACK_MEDIA_META_FILE, compress_type=compression) + except: + sys.exit("error writing media info file") + + #also add the other asset file - contain jamtrack title and other description etc... + self.addJamTrackAssetToContainer(self.otherAssetFile,rsa,zfile) + zfile.close() + + #print_info(outfile) + + def generateContainer(self): + self.encodeMediaFiles() + self.encodeCoverArt(); + + self.initContainer() + + #now append file to container + zf = zipfile.ZipFile(self.packageFileName, mode='a') + + if self.encryptedCoverFile: + try: + zf.write(self.encryptedCoverFile.getOutputFileName(), + arcname=self.encryptedCoverFile.getCoverEncryptFilePackageName(0), + compress_type=compression) + except: + sys.exit("error writing cover art info container file") + + trackNum=0 + for mediaFileInfo in self.encryptedMediaFiles: + # store the file in the archive under the track name + try: + zf.write(mediaFileInfo.getOutputFileName(), + arcname=mediaFileInfo.getEncryptFilePackageName(trackNum), + compress_type=compression) + except: + sys.exit("error writing media info container file") + + trackNum = trackNum + 1 + zf.close() + #dump the details of the final package + if DEBUG_ENABLE != 0: + print_info(self.packageFileName) + + def extractMediaMetaData(self,fname,rsa,zf): + # first extract the media_meta file + metadata ='' + for info in zf.infolist(): + if fname in info.filename: + try: + metadata = zf.read(info.filename) + except KeyError: + print 'ERROR: Did not find %s in zip file' % info.filename + sys.exit() + + break + if metadata == '': + print 'No media meta data found in package' + sys.exit() + + metadata = rsa.decryptMesssage(bytes(metadata)) + #print metadata + #convert to to string + estr = ''.join(metadata) + #strip padding + estr = estr.rstrip(b'\0') + if DEBUG_ENABLE: + print estr + + return estr + + def extractCoverArtData(self,fname,zf): + # first extract the media_meta file + metadata ='' + for info in zf.infolist(): + if fname in info.filename: + try: + metadata = zf.read(info.filename) + except KeyError: + print 'ERROR: Did not find %s in zip file' % info.filename + sys.exit() + + break + if metadata == '': + print 'No cover art data found in package' + sys.exit() + + metadata = rsa.decryptMesssage(bytes(metadata)) + #print metadata + #convert to to string + estr = ''.join(metadata) + #strip padding + estr = estr.rstrip(b'\0') + if DEBUG_ENABLE: + print estr + + return estr + + def decryptAesFile(self,aesKey,fdata,fname,tname, padding): + print fname,'->',tname + # write the content to tmp file + outTmpFile = tempfile.NamedTemporaryFile() + outTmpFile.write(fdata) + + track = JKtrack('dec_'+fname) + track.setKeyStr(aesKey) + track.DecodeBufferToFile(fdata,tname,padding) + if COMPARE_PARSEFILE: + # write the encrypted files out + fi = open('enc_'+tname,'wb') + fi.write(fdata); + fi.close() + + if filecmp.cmp(tname,fname): + print 'OK: file %s match %s' % (fname,tname) + else: + print 'BAD: file %s does not match %s' % (fname,tname) + + def parseContainer(self, rsaKeyFile): + # file must be zip file + zf = zipfile.ZipFile(self.packageFileInfo) + + #now decrypt data with rsa public key + rsa = JKrsa() + rsa.loadKey(rsaKeyFile) + + # get the jam package info file + pinfo = self.extractMediaMetaData(JAMTRACK_PACKAGE_INFO_FILE,rsa,zf) + fi = open(JAMTRACK_PACKAGE_INFO_FILE,'wb') + fi.write(pinfo); + fi.close() + + #get the media details + estr = self.extractMediaMetaData(JAMTRACK_MEDIA_META_FILE,rsa,zf) + data = json.loads(estr) + if DEBUG_ENABLE: + pprint(data) + + try: + coverFileInfoArray = data[JAMTRACK_COVER_ART_TAG] + try: + coverFileInfo= coverFileInfoArray[0] + cfile = coverFileInfo['datafile'] + aesKey = coverFileInfo['key'] + fname = coverFileInfo['filename'] + fsize = coverFileInfo['size'] + padding = coverFileInfo['padding'] + try: + fdata = zf.read(cfile) + except KeyError: + print 'ERROR: Did not find %s in zip file' % cfile + sys.exit() + else: + self.decryptAesFile(aesKey,fdata,fname,cfile,padding) + except: + print 'no cover art data' + except: + print 'no cover art' + + #now extract the file + mediaFilesInfo = data["tracks"] + + for mediaFileInfo in mediaFilesInfo: + aesKey = mediaFileInfo['key'] + fname = mediaFileInfo['filename'] + fsize = mediaFileInfo['size'] + tname = mediaFileInfo['trackname'] + padding = mediaFileInfo['padding'] + fdata = '' + try: + fdata = zf.read(tname) + except KeyError: + print 'ERROR: Did not find %s in zip file' % tname + sys.exit() + else: + self.decryptAesFile(aesKey,fdata,fname,tname,padding) + + diff --git a/ruby/lib/py/jam_tracks/jkasset.pyc b/ruby/lib/py/jam_tracks/jkasset.pyc new file mode 100644 index 000000000..efd5ed263 Binary files /dev/null and b/ruby/lib/py/jam_tracks/jkasset.pyc differ diff --git a/ruby/lib/py/jam_tracks/jkcreate.py b/ruby/lib/py/jam_tracks/jkcreate.py new file mode 100755 index 000000000..e2272e17b --- /dev/null +++ b/ruby/lib/py/jam_tracks/jkcreate.py @@ -0,0 +1,249 @@ +import jkasset +import json +import jkasset +from pprint import pprint +import random +import sys, getopt +import jkmedia +import base64 +import tempfile +import argparse +from types import * +import unicodedata + +parser ='' +inputfiles = [] + +def help(): + parser.print_help() + + + +def media_arg(string): + #print 'media_arg:', string + try: + fname,ext = string.split('+') + #print fname + if fname == '': + print "### No music file in command line: ", string + raise argparse.ArgumentTypeError("Track file name empty") + + inputfiles.append(string) + except: + if string: + inputfiles.append(string) + pass + + return string + +def loadTracksFromList(fileName): + try: + lines = [line.strip() for line in open(fileName)] + #print lines + global inputfiles + inputfiles = lines + except: + print "Unable to open media filelist", fileName + help() + sys.exit(-1) + +def process(argv): + #print argv + global parser + parser = argparse.ArgumentParser( + prog='jkcreate', + description='JamTrack Packaging Tool', + epilog="Note: Meta file values may be update with title,sku,etc") + + parser.add_argument("-D", "--verbosity", + help="increase output verbosity", + action="store_true") + + parser.add_argument("-t", "--title", metavar='title', type=str, + help="Title of JamTrack. If not specified, the title in the metafile is used.") + + parser.add_argument("-k", "--sku", type=str, + help="The product SKU to use. If not specified, the SKU in the metafile is used.",required=True,) + + parser.add_argument("-c", "--cover", metavar='coverArtFile', type=str, + help="The cover art file. File should be JPEG or PNG.") + + parser.add_argument("-p", "--pkey", metavar='pkey.pem',type=str, + help="The output public key file. File should be .pem",required=True,) + + parser.add_argument("-s", "--skey", metavar='skey.pem', type=str, + help="The output private key file. File should be .pem",required=True,) + + + parser.add_argument("-m", "--meta",metavar='trackMetaFile', type=str, + help="The input JamTrack JSON metafile file. Optional") + + parser.add_argument("-o", "--ofile", metavar='JamTrackPage', type=str, + help="The output JamTrack file (.jkz)",required=True,) + + parser.add_argument("-I", "--infilesList", metavar='musicTrackFileList', type=str, + help=" -I 'file containing tracks'") + + parser.add_argument("-i", "--infiles", metavar='musictrack', type=media_arg, nargs='*', + help=" -i 'Lumme.ogg+Bass Guitar' -i 'Hydrate-Kenny_Beltrey.ogg+Bass Guitar'",) + + + #parser.print_help() + args = parser.parse_args(argv) + #print args + + try: + if args.verbosity: + jkasset.DEBUG_ENABLE = 1 + jkasset.COMPARE_PARSEFILE = True + #args.sku = random.randint(1, 100000000) + except: + pass + + + try: + if args.infilesList: + loadTracksFromList(args.infilesList) + except: + pass + #create json struct from info + metaJson={} + try: + if args.meta: + #try to load json data + try: + json_data=open(args.meta).read() + + metaJson = json.loads(json_data) + if jkasset.DEBUG_ENABLE : + pprint(metaJson) + except: + print 'Error processing json from file: ', args.meta + help() + sys.exit(-1) + except: + print 'No meta file specified' + pass + + jdata={} + mdata=[] + instdata={} + + if any(metaJson): + try: + #pickup the instrument data passed in - if any + instdata = metaJson["TrackInstrument"] + except: + pass + + i = 0 + trackLength = {} + trackLenStats=[] + + #print inputfiles + + if not any(inputfiles) : + print 'No track files specified ' + help() + sys.exit(-1) + + for mediaFileInfo in inputfiles: + #update track name and instrument name + #print mediaFileInfo + mediaFileInfo.strip(); + mlist = mediaFileInfo.split("+") + mediaFileInfo = mlist[0] + + trackName = 'track_%02d' % (i) + i = i + 1 + mdata.append({"name" : mediaFileInfo, "trackName": trackName}) + + try: + minfo = jkmedia.JKTrackInfo(mediaFileInfo) ; + tinfo={}; + tinfo['bitrate']=minfo.bitrate + tinfo['length'] = minfo.trackLength + trackLenStats.append(minfo.trackLength) + trackLength[trackName]= tinfo + except: + print("Cannot find file or read media information for '%s'"% mediaFileInfo) + #help() + sys.exit(-1) + + try: + instdata[trackName]=mlist[1] + except: + if instdata.has_key(trackName) == False: + print "No instrument for track ",trackName + pass + + try: + if args.title: + try: + title = base64.b64encode(args.title) + metaJson['Title']=title + except: + print 'Error processing title : ', title + help() + sys.exit(-1) + + if args.sku: + try: + metaJson['SKU']=args.sku + except: + print 'Error sku' + help() + sys.exit(-1) + + metaJson['METADATA_VER'] = 1 + + except: + #These are both optional.. + pass + + if any(instdata): + metaJson['TrackInstrument'] = instdata + if len (instdata) > len(mdata): + print "Number of instruments ", len (instdata)," in meta data greater than number of tracks ",len(mdata) + help() + sys.exit(-1) + + if any(trackLength): + metaJson['TrackData'] = trackLength + + #dump the meta data to temp file + + metaTmp = tempfile.NamedTemporaryFile(); + metaTmp.write(json.dumps(metaJson)) + metaTmp.seek(0) + + jdata["tracks"] = mdata + jdata["rsa_priv_file"]= args.skey + jdata["rsa_pub_file"]= args.pkey + jdata["container_file"]= args.ofile + + #jdata["jamktrack_info"]= trackMetaFile + jdata["jamktrack_info"]= metaTmp.name + + try: + jdata["coverart"]= args.cover + except: + print 'No cover art specified' + + data = json.dumps(jdata) + #pprint(data) + + fname = 'jt_metadata.json' + fo = open(fname, 'w') + fo.write(data) + fo.close() + + jas = jkasset.JKasset(fname) + + +if __name__ == "__main__": + random.seed() + process(sys.argv[1:]) + + + diff --git a/ruby/lib/py/jam_tracks/jkmedia.py b/ruby/lib/py/jam_tracks/jkmedia.py new file mode 100644 index 000000000..035abc3a4 --- /dev/null +++ b/ruby/lib/py/jam_tracks/jkmedia.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +import base64 +import os +import binascii +import json +import sys +from optparse import OptionParser +#from import mutagen.ogg +#from import mutagen.oggvorbis + +class JKTrackInfo: + trackLength =0 + bitrate = 0 + mediaFile=''; + + + def __init__(self, fileName): + #get the file extension + self.mediaFile = fileName + fileName, fileExtension = os.path.splitext(self.mediaFile) + audio = '' + if fileExtension == '.ogg' : + from mutagen.oggvorbis import OggVorbis + audio = OggVorbis(self.mediaFile) + elif fileExtension == '.wav' : + from mutagen.wavpack import WavPack + audio = WavPack(self.mediaFile) + elif fileExtension == '.mp3' : + from mutagen.mp3 import MP3 + audio = MP3(self.mediaFile) + elif fileExtension == '.flac' : + from mutagen.flac import FLAC + audio = FLAC(self.mediaFile) + else: + print "Unsupported filetype '",fileExtension, "' for file ", self.mediaFile + raise Exception("Unsupported filetype") + + print audio.info.length, audio.info.bitrate + self.bitrate = audio.info.bitrate + self.trackLength = audio.info.length + + + + diff --git a/ruby/lib/py/jam_tracks/jkrsa.py b/ruby/lib/py/jam_tracks/jkrsa.py new file mode 100755 index 000000000..4d0e38600 --- /dev/null +++ b/ruby/lib/py/jam_tracks/jkrsa.py @@ -0,0 +1,194 @@ +# RSA Cipher +# http://inventwithpython.com/hacking (BSD Licensed) + +from Crypto.Cipher import PKCS1_OAEP +from Crypto.PublicKey import RSA +from Crypto import Random +import base64 +import sys + +class JKrsa: + # IMPORTANT: The block size MUST be less than or equal to the key size! + # (Note: The block size is in bytes, the key size is in bits. There + # are 8 bits in 1 byte.) + #DEFAULT_BLOCK_SIZE = 128-2 # 128 bytes + DEFAULT_BLOCK_SIZE = 64 # 128 bytes + + + RSA_KEYSIZE = 1024 + BYTE_SIZE = 256 # One byte has 256 different values. + RSAKey=''; + PADDING = 0 + PADDING_CHARACTER =b'\0' + encipher=''; + decipher='' + + def __init__(self, operation='encode'): + self.op = operation + + def updatePadSize(self,dlen): + self.PADDING = (self.DEFAULT_BLOCK_SIZE - dlen % self.DEFAULT_BLOCK_SIZE) + + def getPadding(self): + return self.PADDING + + def pad(self,s): + self.updatePadSize(len(s)) + pdata = self.PADDING * self.PADDING_CHARACTER + if len(pdata) != self.PADDING : + sys.exit('Pad data length %d does match %d. Check python pad character size!', + (len(pdata), self.PADDING)) + s = s + pdata + return s + + def EncodeRSA(self,c, s): + #print 'len=',len(s) + estr = self.encipher.encrypt(s) + #print 'elen=',len(estr) + #print estr + return estr + + def DecodeRSA(self,c, e): + #print 'len=',len(e) + str = self.decipher.decrypt(e) + return str + + def generateKey(self, privateFileName='pri_Key.pem', publicFileName='pub_Key.pem'): + self.RSAKey = RSA.generate(self.RSA_KEYSIZE) + + public_key = self.RSAKey.publickey().exportKey("PEM") + private_key = self.RSAKey.exportKey("PEM") + + if privateFileName: + f = open(privateFileName,'w') + f.write(self.RSAKey.exportKey("PEM")) + f.close() + + f = open(privateFileName,'r') + skey = RSA.importKey(f.read()) + self.decipher = PKCS1_OAEP.new(skey) + f.close() + + if publicFileName: + f = open(publicFileName,'w') + f.write(self.RSAKey.publickey().exportKey("PEM")) + f.close() + + f = open(publicFileName,'r') + pkey = RSA.importKey(f.read()) + self.encipher = PKCS1_OAEP.new(pkey) + f.close() + + return private_key, public_key + + def loadKey(self, fileName='pri_Key.pem'): + f = open(fileName,'r') + self.RSAKey = RSA.importKey(f.read()) + self.decipher = PKCS1_OAEP.new(self.RSAKey) + f.close() + + def WriteFileData(self,outfile,data): + #ddata = base64.b64decode(data) + fi = open(outfile,'wb') + fi.write(data); + + def LoadFileData(self, infile): + fi = open(infile,'rb') + data = fi.read(); + #edata = base64.b64encode(data) + return data + + def WriteRSAFileData(self, outfile,data): + # These files are assumed to be in cypher text - so we won't b64 encode/decode them + fo = open(outfile,'wb') + #edata = base64.b64encode(data) + fo.write(data) + + + def LoadRSAFileData(self, infile): + # These files are assumed to be in cypher text - so we won't b64 encode/decode them + fi = open(infile,'rb') + data = fi.read(); + return data + + def getBlocksFromText(self,message, blockSize=DEFAULT_BLOCK_SIZE): + # Converts a string message to a list of block integers. Each integer + # represents 128 (or whatever blockSize is set to) string characters. + # messageBytes = message.encode('ascii') # convert the string to bytes + blockInts = [] + blockInts = [message[i:i+blockSize] for i in range(0, len(message), blockSize)] + return blockInts + + + def encryptMesssage(self, message, blockSize=DEFAULT_BLOCK_SIZE): + keySize = self.RSAKey.size() + 1; + + # Check that key size is greater than block size. + if keySize < blockSize * 8: # * 8 to convert bytes to bits + sys.exit('ERROR: Block size is %d bits and key size is %d bits.' + ' The RSA cipher requires the block size to be equal to' + ' or less than the key size. Either decrease the' + ' block size or use different keys.' % (blockSize * 8, keySize)) + + if len(message) % blockSize : # * 8 to convert bytes to bits + sys.exit('ERROR: Block size is %d and data size is %d .' + ' The RSA cipher requires the block size to be equal to' + ' or less than the key size. Either decrease the' + ' block size or use different keys.' % (blockSize, len(message))) + + + encryptedContent = [] + for block in self.getBlocksFromText(message, blockSize): + encryptedContent_ = self.EncodeRSA(self.RSAKey,block); + encryptedContent.extend(encryptedContent_) + + return encryptedContent + + def decryptMesssage(self, message): + #print message + #split in equal chunks + decryptedContent = [] + for block in self.getBlocksFromText(message, self.DEFAULT_BLOCK_SIZE*2): + decryptedContent_ = self.DecodeRSA(self.RSAKey,block); + decryptedContent.extend(decryptedContent_) + + return decryptedContent + + + def encryptAndWriteToFile(self, keyFilename, inFile,outFilename, blockSize=DEFAULT_BLOCK_SIZE): + # Using a key from a key file, encrypt the message and save it to a + # file. Returns the encrypted message string. + self.loadKey(keyFilename) + keySize = self.RSAKey.size()+1; + + # Check that key size is greater than block size. + if keySize < blockSize * 8: # * 8 to convert bytes to bits + sys.exit('ERROR: Block size is %s bits and key size is %s bits.' + ' The RSA cipher requires the block size to be equal to' + ' or less than the key size. Either decrease the' + ' block size or use different keys.' % (blockSize * 8, keySize)) + + # Encrypt the message + message = self.LoadFileData(inFile); + message = self.pad(message) + + encryptedContent = self.encryptMesssage(message); + self.WriteRSAFileData(outFilename,encryptedContent) + # Also return the encrypted string. + #return encryptedContent + + + def readFromFileAndDecrypt(self, keyFilename, inFile, outFilename): + # Using a key from a key file, read an encrypted message from a file + # and then decrypt it. Returns the decrypted message string. + self.loadKey(keyFilename) + + # Read in the message length and the encrypted message from the file. + message = self.LoadRSAFileData(inFile) + + decryptMessage = self.decryptMesssage(message) + self.WriteFileData(outFilename,decryptMessage) + + + + diff --git a/ruby/lib/py/jam_tracks/jktrack.py b/ruby/lib/py/jam_tracks/jktrack.py new file mode 100755 index 000000000..3e0dcd4b3 --- /dev/null +++ b/ruby/lib/py/jam_tracks/jktrack.py @@ -0,0 +1,104 @@ +import jkaes +import tempfile +from jkaes import JKaes +import os +class JKtrack: + mediaFile = '' + Name = '' + aesInKeyFile='' + aesCipher = '' + outTmpFile ='' + outPutFile = '' + def __init__(self, fileName, title='Unknown', inKeyFile=''): + self.mediaFile = fileName + self.Name = title + self.aesInKeyFile = inKeyFile + self.aesCipher = JKaes(self.mediaFile, self.aesInKeyFile) + + def getOutputFileName(self): + return self.outPutFile; + + def getKeyStr(self): + return self.aesCipher.getKeyStr(); + + def getEncryptPadding(self): + return self.aesCipher.getPadding() + + def setKeyStr(self,key): + self.aesCipher.setKey(key); + + def getKeyModeStr(self): + return self.aesCipher.getKeyModeStr() + + def getCoverEncryptFilePackageName(self,instance=-1): + #preserve the extentsion on the original name + fileName, fileExtension = os.path.splitext(self.mediaFile) + if instance != -1: + nm = "cover_%02d%s" % (instance,fileExtension) + return nm + else: + nm = os.path.basename(self.mediaFile) + return nm + + def getEncryptFilePackageName(self,instance=-1): + #preserve the extentsion on the original name + fileName, fileExtension = os.path.splitext(self.mediaFile) + if instance != -1: + nm = "track_%02d%s" % (instance,fileExtension) + return nm + else: + nm = os.path.basename(self.mediaFile) + return nm + + def Encode(self, outFile): + self.outPutFile = outFile + self.aesCipher.EncodeFileAndWrite( self.mediaFile, outFile) + + def EncodeToTmpFile(self): + self.outTmpFile = tempfile.NamedTemporaryFile() + self.Encode(self.outTmpFile.name) + + def EncodeWithKeyToFile(self,keyFile,outFile): + self.aesCipher.readAesKeyFile(keyFile) + self.Encode(outFile) + + def Decode(self, outFile): + self.outPutFile = outFile + self.aesCipher.DecodeFileAndWrite( self.mediaFile, outFile) + + def DecodeToTmpFile(self): + self.outTmpFile = tempfile.NamedTemporaryFile() + self.Decode(self.outTmpFile.name) + + def DecodeWithKeyToFile(self,keyFile,outFile): + self.aesCipher.readAesKeyFile(keyFile) + self.Decode(outFile) + + + def DecodeBufferToFile(self,msg,outfile,padding=0): + dmsg = self.aesCipher.DecodeMsg(msg) + fi = open(outfile,'wb') + fi.write(dmsg); + if padding > 0: + #trunckage the file by + fi.seek(-padding, os.SEEK_END) + fi.truncate() + fi.close() + return dmsg + + def DecodeU64Buffer(self,msg): + return self.aesCipher.DecodeU64Msg(msg) + + def setKey(self, keyFile): + self.aesCipher.readAesKeyFile(keyFile) + + def saveKey(self,keyFile): + self.aesCipher.writeAesKeytoFile(keyFile) + + def saveEncFileCopy(self,outfile): + fi = open(self.outPutFile,'rb') + fi.seek(0) + dmsg = fi.read(); + fo = open(outfile,'wb') + fo.write(dmsg); + return dmsg \ No newline at end of file diff --git a/ruby/lib/py/jam_tracks/jktrack.pyc b/ruby/lib/py/jam_tracks/jktrack.pyc new file mode 100644 index 000000000..d065cbbe6 Binary files /dev/null and b/ruby/lib/py/jam_tracks/jktrack.pyc differ diff --git a/ruby/lib/py/jam_tracks/jkunpack.py b/ruby/lib/py/jam_tracks/jkunpack.py new file mode 100755 index 000000000..1544fb6ab --- /dev/null +++ b/ruby/lib/py/jam_tracks/jkunpack.py @@ -0,0 +1,56 @@ +import jkasset +import json +import jkasset +from pprint import pprint +import random +import sys, getopt +import base64 +import tempfile +import argparse +from types import * +import unicodedata + +parser ='' +def help(): + parser.print_help() + #print sys.argv[0],'[-D] -d -i -s ' + + +def process(argv): + + global parser + parser = argparse.ArgumentParser( + prog='jkunpack', + description='JamTrack UnPackaging Tool', + epilog="Note: Files may be overwritten during unpacking") + + parser.add_argument("-D", "--verbosity", + help="increase output verbosity", + action="store_true") + + parser.add_argument("-s", "--skey", metavar='skey.pem', type=str, + help="The private key file. File should be .pem",required=True) + + parser.add_argument("-i", "--ifile", metavar='JamTrackPage', type=str, + help="The input JamTrack file (.jkz)",required=True) + + parser.print_help() + args = parser.parse_args(argv) + print args + + try: + if args.verbosity: + jkasset.DEBUG_ENABLE = 1 + jkasset.COMPARE_PARSEFILE = True + except: + pass + + + jas = jkasset.JKasset(args.ifile,'decode',args.skey) + +if __name__ == "__main__": + random.seed() + process(sys.argv[1:]) + + + diff --git a/ruby/lib/py/jam_tracks/jkzify.rb b/ruby/lib/py/jam_tracks/jkzify.rb new file mode 100755 index 000000000..c5ef594fd --- /dev/null +++ b/ruby/lib/py/jam_tracks/jkzify.rb @@ -0,0 +1,98 @@ +#!/usr/bin/env ruby +# +# Usage: jkzify [path to JamTrack WAVs] [destination for jkz and key files] +# +# Please review the comments, this script needs improvement -- I just threw it together to help me validate jkcreate.py +# + +wd = Dir.getwd +tracks_location = ARGV.shift +output_location = ARGV.shift +script_location = File.dirname(File.expand_path(__FILE__)) + + + +# first try to catch any issues with input arguments +def halt msg; warn msg; exit; end +halt "usage: jkzify path/to/wavs/ path/to/output" unless tracks_location && output_location +halt "path not found" unless File.exist?(tracks_location) && File.exist?(output_location) +# end + + + +# step 1: gather metadata from the track/folder names (and user input) +title = tracks_location.split('/').last.gsub(/'/,'') +puts "i think this track is called \"#{title}\"..." +shortname = title.gsub(/[aeiou\s]/,'').squeeze[0...8].downcase +puts "i'll call it #{shortname} for now" +puts "what sku do you want for this JamTrack? (press ENTER for '#{shortname}')" +input = STDIN.gets.chomp.strip +sku = input.empty? ? shortname : input + + + +# step 2: move WAVs to our own workspace +def renamed(t); t.gsub(/\s|'/,'_'); end #little helper + +Dir.chdir wd +`rm -rf #{shortname}` if File.exist? shortname +`mkdir #{shortname}` + +Dir.chdir tracks_location +Dir.glob('*.wav') do |track| + new_name = renamed track + `cp "#{track}" "#{wd}/#{shortname}/#{new_name}"` + puts "Copied from " + track + " to #{wd}/#{shortname}/" + new_name +end +Dir.chdir "#{wd}/#{shortname}" + + + +# step 3: generate OGGs from WAVs +oggs_location = title.downcase.gsub('\'','').split.join('_') + '_oggs/' +puts "generating OGGs in a separate folder #{oggs_location} (this may take some time)" +`mkdir #{oggs_location}` unless File.exist?(oggs_location) +Dir.glob('*.wav') do |track| + input = track + output = "#{oggs_location}#{File.basename(track, '.wav')}.ogg" + #puts "sox #{input} #{output}" + `sox #{input} #{output}` +end + + +# step 4: get all track parameters (names and instruments) +track_params = {} +track_files = [] +suffix = '\.wav' +Dir.foreach tracks_location do |track| + next if track =~ /Master Mix/i + next unless track =~ /#{suffix}$/i + print '.' + track.gsub!(/#{suffix}$/i, '') + instrument = track.split('-').drop(1).map(&:strip).join(' ') + parameter = "#{shortname}/#{oggs_location}#{renamed track}.ogg+#{instrument}" + #parameter.gsub!(/ /, '\ ') #spaces in filenames + track_params[instrument] = "-i '#{parameter}'" + track_files << parameter +end +puts "OK, #{track_params.length} tracks found" + + + +# step 5: create list of track titles +Dir.chdir wd +tracks_filename = "#{shortname}.txt" +File.open(tracks_filename, 'w+') { |f| f.puts track_files } + + + +# step 6: run jkcreate.py given all these parameters +public_key = "#{output_location}#{shortname}-pub.pem" +private_key = "#{output_location}#{shortname}-priv.pem" +output_jkz = "#{output_location}#{shortname}.jkz" +#puts "python jkcreate.py -D -k #{sku} -c art.png -p #{public_key} -s #{private_key} -I #{tracks_filename} -o #{output_jkz} -t '#{title}'" +`python jkcreate.py -D -k #{sku} -c art.png -p #{public_key} -s #{private_key} -I #{tracks_filename} -o #{output_jkz} -t '#{title}'` + + + +Dir.chdir wd \ No newline at end of file