2 changed files with 157 additions and 7 deletions
@ -1,5 +1,144 @@ |
|||||
|
import os |
||||
|
import posixpath |
||||
|
import shutil |
||||
|
from send2trash import send2trash |
||||
|
from pyadb.internal import config |
||||
|
from pyadb.internal.directory import Directory, get_unmodified |
||||
from pyadb.internal.cli_wrap import AdbWrapper |
from pyadb.internal.cli_wrap import AdbWrapper |
||||
|
|
||||
|
|
||||
|
def merge(src, dst, log=False): |
||||
|
if not os.path.exists(dst): |
||||
|
return False |
||||
|
ok = True |
||||
|
for path, dirs, files in os.walk(src): |
||||
|
relPath = os.path.relpath(path, src) |
||||
|
destPath = os.path.join(dst, relPath) |
||||
|
if not os.path.exists(destPath): |
||||
|
os.makedirs(destPath) |
||||
|
for file in files: |
||||
|
destFile = os.path.join(destPath, file) |
||||
|
if os.path.isfile(destFile): |
||||
|
if log: |
||||
|
print("Skipping existing file: " + |
||||
|
os.path.join(relPath, file)) |
||||
|
ok = False |
||||
|
continue |
||||
|
srcFile = os.path.join(path, file) |
||||
|
shutil.move(srcFile, destFile) |
||||
|
for path, dirs, files in os.walk(src, False): |
||||
|
if len(files) == 0 and len(dirs) == 0: |
||||
|
os.rmdir(path) |
||||
|
return ok |
||||
|
|
||||
|
|
||||
class FileSystem(AdbWrapper): |
class FileSystem(AdbWrapper): |
||||
|
STAT_TYPE = 1 |
||||
|
|
||||
|
def stat(self, file): |
||||
|
"""Returns information on file""" |
||||
|
# %a Access bits (octal) |%A Access bits (flags)|%b Size/512 |
||||
|
# %B Bytes per %b (512) |%d Device ID (dec) |%D Device ID (hex) |
||||
|
# %f All mode bits (hex) |%F File type |%g Group ID |
||||
|
# %G Group name |%h Hard links |%i Inode |
||||
|
# %m Mount point |%n Filename |%N Long filename |
||||
|
# %o I/O block size |%s Size (bytes) |%t Devtype major |
||||
|
# %T Devtype minor (hex) |%u User ID |%U User name |
||||
|
# %x Access time |%X Access unix time |%y File write time |
||||
|
# %Y File write unix time|%z Dir change time |%Z Dir change time |
||||
|
|
||||
|
# The valid format escape sequences for filesystems: |
||||
|
# %a Available blocks |%b Total blocks |%c Total inodes |
||||
|
# %d Free inodes |%f Free blocks |%i File system ID |
||||
|
# %l Max filename length |%n File name |%s Fragment size |
||||
|
# %S Best transfer size |%t FS type (hex) |%T FS type |
||||
|
command = 'stat -c "%A;%F;%U;%G" {};echo $?'.format(file) |
||||
|
res = self.sudo(command, output="out") |
||||
|
output, res = res.split('\n') |
||||
|
if res == '0': |
||||
|
return output.split(';') |
||||
|
|
||||
|
def exists(self, file): |
||||
|
return self.stat(file) is not None |
||||
|
|
||||
|
def isfile(self, file): |
||||
|
return self.stat(file)[FileSystem.STAT_TYPE] == 'file' |
||||
|
|
||||
|
def isdir(self, file): |
||||
|
return self.stat(file)[FileSystem.STAT_TYPE] == 'directory' |
||||
|
|
||||
|
def islink(self, file): |
||||
|
return self.stat(file)[FileSystem.STAT_TYPE] == 'symbolic link' |
||||
|
|
||||
|
def delete(self, path): |
||||
|
return self.sudo(["rm", "-rf", path], output="out") |
||||
|
|
||||
|
def pull(self, remote, local, override=False): |
||||
|
parent = os.path.dirname(local) |
||||
|
if not os.path.exists(parent): |
||||
|
os.makedirs(parent) |
||||
|
if os.path.exists(local): |
||||
|
if override: |
||||
|
if os.path.isdir(local): |
||||
|
shutil.rmtree(local) |
||||
|
else: |
||||
|
os.remove(local) |
||||
|
else: |
||||
|
raise FileExistsError |
||||
|
|
||||
|
self.execute(['pull', remote, local]) |
||||
|
|
||||
|
def push(self, local, remote): |
||||
|
self.execute(['push', remote, local]) |
||||
|
|
||||
|
def merge_local_with_remote(self, remote, local, dry=True, delete=False): |
||||
|
"""Merge local directory with directory on phone, optionally deleting""" # noqa |
||||
|
stdout, stderr = self.execute( |
||||
|
['cd', remote, ';', 'find', '.', '-type', 'f'], |
||||
|
output_streams=True |
||||
|
) |
||||
|
listing = stdout.read().decode().rstrip().split(config.LINEFEED) |
||||
|
remote_dirtree = Directory.from_dir_listing( |
||||
|
listing, is_listening=True) |
||||
|
prev_working = os.getcwd() |
||||
|
os.chdir(local) |
||||
|
for root, dirs, files in os.walk("."): |
||||
|
new_root = root.split(os.sep) |
||||
|
parent = remote_dirtree.traverse( |
||||
|
new_root, create_intermediate=False) |
||||
|
for file in files: |
||||
|
fp = os.path.join(root, file) |
||||
|
try: |
||||
|
parent.remove_child(file) |
||||
|
except FileNotFoundError: |
||||
|
if delete: |
||||
|
if dry: |
||||
|
print("Removing:", fp) |
||||
|
else: |
||||
|
send2trash(fp) |
||||
|
|
||||
|
os.chdir(prev_working) |
||||
|
for file in get_unmodified(remote_dirtree): |
||||
|
if config.IS_WINDOWS: |
||||
|
local_path = os.path.join(local, file.replace('/', os.sep)) |
||||
|
else: |
||||
|
local_path = os.path.join(local, file) |
||||
|
remote_path = posixpath.join(remote, file) |
||||
|
if dry: |
||||
|
print(remote_path, local_path, sep='->') |
||||
|
else: |
||||
|
self.pull(remote_path, local_path) |
||||
|
|
||||
|
|
||||
|
class NormalDevice(FileSystem): |
||||
|
"""Device model that represents when the device is booted in an os |
||||
|
|
||||
|
""" |
||||
|
pass |
||||
|
|
||||
|
|
||||
|
class RecoveryMode(FileSystem): |
||||
|
"""Device model that represents wehn the device is in recovery mode |
||||
|
|
||||
|
""" |
||||
pass |
pass |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue