Skip to content

Commit

Permalink
Rename pmca.py to pmca-console.py, move commands to module
Browse files Browse the repository at this point in the history
  • Loading branch information
ma1co committed Jul 17, 2016
1 parent a39bf1b commit 01dd0bc
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 115 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ install:

script:
# Run pyinstaller
- pyinstaller pmca.spec
- pyinstaller pmca-console.spec

deploy:
# Deploy tagged releases
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,27 @@ There are two ways to install apps on your camera. Be sure it is connected over
**Go to [sony-pmca.appspot.com](https://sony-pmca.appspot.com/) to try it out!** You can upload your own apps and install them to your camera using the official Sony browser plugin. Since other browser vendors are disabling NPAPI plugins, please try it using **Internet Explorer**.

### Local installer ###
Download the [latest release](https://github.com/ma1co/Sony-PMCA-RE/releases/latest) (Windows or OS X) or clone this repository. Run `pmca --help` for more information.
Download the [latest release](https://github.com/ma1co/Sony-PMCA-RE/releases/latest) (Windows or OS X) or clone this repository. Run `pmca-console --help` for more information.

#### Usage ####
* Test the USB connection to your camera (the result is written to the specified file):

pmca install -o outfile.txt
pmca-console install -o outfile.txt

* Install an app on your camera (the app is uploaded and served from Google appengine):

pmca install -f app.apk
pmca-console install -f app.apk

* Install an app using a local web server:

pmca install -s "" -f app.apk
pmca-console install -s "" -f app.apk

* Download apps from the official Sony app store (interactive):

pmca market
pmca-console market

#### Windows drivers ####
On Windows, the choice defaults to the default Windows USB drivers. If you want to use libusb on Windows, you'll have to install generic drivers for your camera using [Zadig](http://zadig.akeo.ie/) (select *libusb-win32*). You can then run `pmca install -d libusb`.
On Windows, the choice defaults to the default Windows USB drivers. If you want to use libusb on Windows, you'll have to install generic drivers for your camera using [Zadig](http://zadig.akeo.ie/) (select *libusb-win32*). You can then run `pmca-console install -d libusb`.

## Is it safe? ##
This is an experiment in a very early stage. All information has been found through reverse engineering. Even though everything worked fine for our developers, it could cause harm to your hardware. If you break your camera, you get to keep both pieces. **We won't take any responsibility.**
Expand All @@ -41,7 +41,7 @@ You can try your standard Android apps from any app store, they should work more
If you want to develop your custom app, feel free to do so. Debug and release certificates are accepted by the camera. There are a few special Sony APIs which allow you to take advantage of the features of your camera. Have a look at the [PMCADemo](https://github.com/ma1co/PMCADemo) project where we try to make sense of those.

## About this repository ##
* **pmca.py**: Source for the USB installer console application. See the releases page for pyinstaller builds for Windows and OS X.
* **pmca-console.py**: Source for the USB installer console application. See the releases page for pyinstaller builds for Windows and OS X.
* **main.py**: The source code for the Google App Engine website served at [sony-pmca.appspot.com](https://sony-pmca.appspot.com/).
* **docs**: Technical documentation

Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ build: off

after_test:
# Run pyinstaller
- pyinstaller pmca.spec
- pyinstaller pmca-console.spec

artifacts:
- path: dist\*
Expand Down
13 changes: 8 additions & 5 deletions pmca.spec → build.spec
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# Run `pyinstaller pmca.spec` to generate an executable
# This file is used by other spec files

import os.path, subprocess, sys
import os, subprocess, sys

# Get version from git
version = subprocess.check_output(['git', 'describe', '--always', '--tags']).strip()

# Generate filename
suffix = {'linux2': '-linux', 'win32': '-win', 'darwin': '-osx'}
output = 'pmca-' + subprocess.check_output(['git', 'describe', '--always', '--tags']).strip() + suffix.get(sys.platform, '')
output += '-' + version + suffix.get(sys.platform, '')

# Analyze files
a = Analysis(['pmca.py'], excludes=['numpy'], datas=[('certs/*', 'certs')])# Don't let comtypes include numpy
a = Analysis([input], excludes=['numpy'], datas=[('certs/*', 'certs')])# Don't let comtypes include numpy
a.binaries = [((os.path.basename(name) if type == 'BINARY' else name), path, type) for name, path, type in a.binaries]# libusb binaries are not found in subdirs

# Generate executable
pyz = PYZ(a.pure, a.zipped_data)
exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name=output)
exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name=output, console=console)
44 changes: 44 additions & 0 deletions pmca-console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python
"""A command line application to install apps on Android-enabled Sony cameras"""
import argparse

import config
from pmca.commands.market import *
from pmca.commands.usb import *
from pmca import spk

def main():
"""Command line main"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='command', title='commands')
info = subparsers.add_parser('info', description='Display information about the camera connected via USB')
info.add_argument('-d', dest='driver', choices=['libusb', 'windows'], help='specify the driver')
install = subparsers.add_parser('install', description='Installs an apk file on the camera connected via USB. The connection can be tested without specifying a file.')
install.add_argument('-s', dest='server', help='hostname for the remote server (set to empty to start a local server)', default=config.appengineServer)
install.add_argument('-d', dest='driver', choices=['libusb', 'windows'], help='specify the driver')
install.add_argument('-o', dest='outFile', type=argparse.FileType('w'), help='write the output to this file')
install.add_argument('-f', dest='apkFile', type=argparse.FileType('rb'), help='the apk file to install')
market = subparsers.add_parser('market', description='Download apps from the official Sony app store')
market.add_argument('-t', dest='token', help='Specify an auth token')
market = subparsers.add_parser('apk2spk', description='Convert apk to spk')
market.add_argument('inFile', metavar='app.apk', type=argparse.FileType('rb'), help='the apk file to convert')
market.add_argument('outFile', metavar='app' + spk.constants.extension, type=argparse.FileType('wb'), help='the output spk file')
market = subparsers.add_parser('spk2apk', description='Convert spk to apk')
market.add_argument('inFile', metavar='app' + spk.constants.extension, type=argparse.FileType('rb'), help='the spk file to convert')
market.add_argument('outFile', metavar='app.apk', type=argparse.FileType('wb'), help='the output apk file')

args = parser.parse_args()
if args.command == 'info':
infoCommand(config.appengineServer, args.driver)
elif args.command == 'install':
installCommand(args.server, args.driver, args.apkFile, args.outFile)
elif args.command == 'market':
marketCommand(args.token)
elif args.command == 'apk2spk':
args.outFile.write(spk.dump(args.inFile.read()))
elif args.command == 'spk2apk':
args.outFile.write(spk.parse(args.inFile.read()))


if __name__ == '__main__':
main()
7 changes: 7 additions & 0 deletions pmca-console.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Run `pyinstaller pmca-console.spec` to generate an executable

input = 'pmca-console.py'
output = 'pmca-console'
console = True

execfile('build.spec')
Empty file added pmca/commands/__init__.py
Empty file.
49 changes: 49 additions & 0 deletions pmca/commands/market.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from getpass import getpass
import os
import re

from .. import marketclient
from .. import spk

def marketCommand(token=None):
if not token:
print 'Please enter your Sony Entertainment Network credentials\n'
email = raw_input('Email: ')
password = getpass('Password: ')
token = marketclient.login(email, password)
if token:
print 'Login successful. Your auth token (use with the -t option):\n%s\n' % token
else:
print 'Login failed'
return

devices = marketclient.getDevices(token)
print '%d devices found\n' % len(devices)

apps = []
for device in devices:
print '%s (%s)' % (device.name, device.serial)
for app in marketclient.getApps(device.deviceid):
if not app.price:
apps.append((device.deviceid, app.id))
print ' [%2d] %s' % (len(apps), app.name)
print ''

if apps:
while True:
i = int(raw_input('Enter number of app to download (0 to exit): '))
if i == 0:
break
app = apps[i - 1]
print 'Downloading app %s' % app[1]
spkName, spkData = marketclient.download(token, app[0], app[1])
fn = re.sub('(%s)?$' % re.escape(spk.constants.extension), '.apk', spkName)
data = spk.parse(spkData)

if os.path.exists(fn):
print 'File %s exists already' % fn
else:
with open(fn, 'wb') as f:
f.write(data)
print 'App written to %s' % fn
print ''
108 changes: 9 additions & 99 deletions pmca.py → pmca/commands/usb.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
#!/usr/bin/env python
"""A command line application to install apps on Android-enabled Sony cameras"""

import argparse
from getpass import getpass
import json
import os, os.path
import re
import os
import sys
import time

import config
from pmca import installer
from pmca import marketclient
from pmca.marketserver.server import *
from pmca import spk
from pmca.usb import *
from pmca.usb.driver import *
from pmca.usb.sony import *
from .. import installer
from ..marketserver.server import *
from ..usb import *
from ..usb.driver import *
from ..usb.sony import *

scriptRoot = getattr(sys, '_MEIPASS', os.path.dirname(__file__))
scriptRoot = getattr(sys, '_MEIPASS', os.path.dirname(__file__) + '/../..')


def printStatus(status):
Expand Down Expand Up @@ -73,10 +64,10 @@ def importDriver(driverName=None):
# Import the specified driver
if driverName == 'libusb':
print 'Using libusb'
import pmca.usb.driver.libusb as driver
from ..usb.driver import libusb as driver
elif driverName == 'windows':
print 'Using Windows drivers'
import pmca.usb.driver.windows as driver
from ..usb.driver import windows as driver
else:
raise Exception('Unknown driver')

Expand Down Expand Up @@ -171,84 +162,3 @@ def installCommand(host=None, driverName=None, apkFile=None, outFile=None):
raise Exception('Timeout')

installApp(device, host, apkFile, outFile)


def marketCommand(token=None):
if not token:
print 'Please enter your Sony Entertainment Network credentials\n'
email = raw_input('Email: ')
password = getpass('Password: ')
token = marketclient.login(email, password)
if token:
print 'Login successful. Your auth token (use with the -t option):\n%s\n' % token
else:
print 'Login failed'
return

devices = marketclient.getDevices(token)
print '%d devices found\n' % len(devices)

apps = []
for device in devices:
print '%s (%s)' % (device.name, device.serial)
for app in marketclient.getApps(device.deviceid):
if not app.price:
apps.append((device.deviceid, app.id))
print ' [%2d] %s' % (len(apps), app.name)
print ''

if apps:
while True:
i = int(raw_input('Enter number of app to download (0 to exit): '))
if i == 0:
break
app = apps[i - 1]
print 'Downloading app %s' % app[1]
spkName, spkData = marketclient.download(token, app[0], app[1])
fn = re.sub('(%s)?$' % re.escape(spk.constants.extension), '.apk', spkName)
data = spk.parse(spkData)

if os.path.exists(fn):
print 'File %s exists already' % fn
else:
with open(fn, 'wb') as f:
f.write(data)
print 'App written to %s' % fn
print ''


def main():
"""Command line main"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='command', title='commands')
info = subparsers.add_parser('info', description='Display information about the camera connected via USB')
info.add_argument('-d', dest='driver', choices=['libusb', 'windows'], help='specify the driver')
install = subparsers.add_parser('install', description='Installs an apk file on the camera connected via USB. The connection can be tested without specifying a file.')
install.add_argument('-s', dest='server', help='hostname for the remote server (set to empty to start a local server)', default=config.appengineServer)
install.add_argument('-d', dest='driver', choices=['libusb', 'windows'], help='specify the driver')
install.add_argument('-o', dest='outFile', type=argparse.FileType('w'), help='write the output to this file')
install.add_argument('-f', dest='apkFile', type=argparse.FileType('rb'), help='the apk file to install')
market = subparsers.add_parser('market', description='Download apps from the official Sony app store')
market.add_argument('-t', dest='token', help='Specify an auth token')
market = subparsers.add_parser('apk2spk', description='Convert apk to spk')
market.add_argument('inFile', metavar='app.apk', type=argparse.FileType('rb'), help='the apk file to convert')
market.add_argument('outFile', metavar='app' + spk.constants.extension, type=argparse.FileType('wb'), help='the output spk file')
market = subparsers.add_parser('spk2apk', description='Convert spk to apk')
market.add_argument('inFile', metavar='app' + spk.constants.extension, type=argparse.FileType('rb'), help='the spk file to convert')
market.add_argument('outFile', metavar='app.apk', type=argparse.FileType('wb'), help='the output apk file')

args = parser.parse_args()
if args.command == 'info':
infoCommand(config.appengineServer, args.driver)
elif args.command == 'install':
installCommand(args.server, args.driver, args.apkFile, args.outFile)
elif args.command == 'market':
marketCommand(args.token)
elif args.command == 'apk2spk':
args.outFile.write(spk.dump(args.inFile.read()))
elif args.command == 'spk2apk':
args.outFile.write(spk.parse(args.inFile.read()))


if __name__ == '__main__':
main()
3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
comtypes
pycrypto
# use pyinstaller 3.1.1 for now, 3.2 has some bugs
pyinstaller==3.1.1
pyinstaller
pyusb

0 comments on commit 01dd0bc

Please sign in to comment.