210 lines
7.9 KiB
Python
Executable file
210 lines
7.9 KiB
Python
Executable file
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
# gPodder - A media aggregator and podcast client
|
|
# Copyright (c) 2005-2011 Thomas Perl and the gPodder Team
|
|
#
|
|
# gPodder is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# gPodder is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
|
|
# gpodder-backup - A backup/restore utility for gPodder user data
|
|
# by Thomas Perl <thp@gpodder.org>; 2009-02-14
|
|
|
|
|
|
"""
|
|
This utility can be used to create a dump of the current gPodder
|
|
data (configuration files + downloads), optionally skipping the
|
|
real contents of the download folder (for submitting your data
|
|
to a bug report without having to transfer lots of data). Modes:
|
|
|
|
* Create (--create) a new archive from the current data
|
|
* Extract (--extract) a previously-created archive
|
|
* Purge (--purge) the current data ("start out fresh")
|
|
"""
|
|
|
|
__version__ = '1.0'
|
|
|
|
from ConfigParser import ConfigParser
|
|
from optparse import OptionParser
|
|
from StringIO import StringIO
|
|
|
|
import sys
|
|
import os
|
|
import subprocess
|
|
import shutil
|
|
import tempfile
|
|
|
|
MANIFEST_NAME = 'manifest'
|
|
CONFIG_DIR = '~/.config/gpodder'
|
|
DOWNLOAD_FOLDER = 'DOWNLOADS'
|
|
CONFIG_FOLDER = 'CONFIG'
|
|
|
|
def implodeuser(s):
|
|
"""Does the reverse of os.path.expanduser"""
|
|
home = os.path.expanduser('~/')
|
|
if s.startswith(home):
|
|
return os.path.join('~', s[len(home):])
|
|
else:
|
|
return s
|
|
|
|
class gPodderConfig(ConfigParser):
|
|
"""A simple gpodder.conf-reading class
|
|
|
|
This class reads CONFIGFILE and then allows to
|
|
access all configuration options as attributes
|
|
of the object (e.g. config.download_dir)
|
|
"""
|
|
CONFIGFILE = CONFIG_DIR + '/gpodder.conf'
|
|
SECTION = 'gpodder-conf-1'
|
|
|
|
def __init__(self):
|
|
ConfigParser.__init__(self)
|
|
self.read(os.path.expanduser(self.CONFIGFILE))
|
|
assert self.has_section(self.SECTION)
|
|
|
|
def __getattr__(self, key):
|
|
return self.get(self.SECTION, key)
|
|
|
|
def store_gpodder_config(self):
|
|
fp = open(os.path.expanduser(self.CONFIGFILE), 'w')
|
|
self.write(fp)
|
|
fp.close()
|
|
|
|
def do_purge():
|
|
print 'Purging:'
|
|
config_dir = os.path.expanduser(CONFIG_DIR)
|
|
if os.path.exists(config_dir):
|
|
if os.path.exists(os.path.expanduser(gPodderConfig.CONFIGFILE)):
|
|
config = gPodderConfig()
|
|
download_dir = config.download_dir
|
|
if os.path.exists(download_dir):
|
|
print ' Downloads in', download_dir
|
|
shutil.rmtree(download_dir)
|
|
print ' Configuration in', config_dir
|
|
shutil.rmtree(config_dir)
|
|
else:
|
|
print ' Nothing (already purged?)'
|
|
print 'done.'
|
|
|
|
def extract_archive(backup_filename, download_destination=None):
|
|
if not os.path.exists(backup_filename):
|
|
print 'File does not exist.'
|
|
sys.exit(-1)
|
|
|
|
config_dir = os.path.expanduser(CONFIG_DIR)
|
|
print 'Extracting config to %s' % config_dir
|
|
if not os.path.exists(config_dir):
|
|
os.makedirs(config_dir)
|
|
tar = subprocess.Popen(['tar', 'xzvf', backup_filename, '-C', config_dir,
|
|
'--strip', '1', CONFIG_FOLDER])
|
|
tar.wait()
|
|
print 'DONE.'
|
|
|
|
print 'Getting manifest'
|
|
tar = subprocess.Popen(['tar', 'xzf', backup_filename, '-O', MANIFEST_NAME],
|
|
stdout=subprocess.PIPE)
|
|
(manifest_data, stderr_unused) = tar.communicate()
|
|
manifest = ConfigParser()
|
|
manifest.readfp(StringIO(manifest_data))
|
|
if download_destination is None:
|
|
download_destination = os.path.expanduser(manifest.get(MANIFEST_NAME, 'download_dir'))
|
|
# update the "download_dir" setting in gpodder.conf,
|
|
# because we are extracting downloads somewhere else
|
|
gpocfg = gPodderConfig()
|
|
gpocfg.set(gPodderConfig.SECTION, 'download_dir', os.path.abspath(download_destination))
|
|
gpocfg.store_gpodder_config()
|
|
|
|
print 'Extracting downloads to %s' % download_destination
|
|
if not os.path.exists(download_destination):
|
|
os.makedirs(download_destination)
|
|
tar = subprocess.Popen(['tar', 'xzvf', backup_filename, '-C', download_destination,
|
|
'--strip', '1', DOWNLOAD_FOLDER])
|
|
tar.wait()
|
|
print 'DONE.'
|
|
|
|
def create_archive(backup_filename, fake_download_dir=True, add_cover_files=False):
|
|
if os.path.exists(backup_filename):
|
|
print 'refusing to overwrite existing file:', backup_filename
|
|
sys.exit(1)
|
|
|
|
tempfolder = tempfile.mkdtemp()
|
|
print 'using', tempfolder, 'to store temporary data'
|
|
config = gPodderConfig()
|
|
download_dir = implodeuser(config.download_dir)
|
|
manifest = ConfigParser()
|
|
manifest.add_section(MANIFEST_NAME)
|
|
configuration_dir = CONFIG_DIR
|
|
for key in ('fake_download_dir', 'download_dir', 'configuration_dir'):
|
|
manifest.set(MANIFEST_NAME, key, locals()[key])
|
|
manifp = open(os.path.join(tempfolder, MANIFEST_NAME), 'w')
|
|
manifest.write(manifp)
|
|
manifp.close()
|
|
if fake_download_dir:
|
|
os.mkdir(os.path.join(tempfolder, DOWNLOAD_FOLDER))
|
|
for dirpath, dirnames, filenames in os.walk(os.path.expanduser(download_dir)):
|
|
new_path = dirpath.replace(os.path.expanduser(download_dir), os.path.join(tempfolder, DOWNLOAD_FOLDER))
|
|
if not os.path.exists(new_path):
|
|
os.makedirs(new_path)
|
|
for filename in filenames:
|
|
if filename == 'folder.jpg' and add_cover_files:
|
|
shutil.copy(os.path.join(dirpath, filename), os.path.join(new_path, filename))
|
|
else:
|
|
open(os.path.join(new_path, filename), 'w').close()
|
|
else:
|
|
os.symlink(os.path.expanduser(download_dir), os.path.join(tempfolder, DOWNLOAD_FOLDER))
|
|
os.symlink(os.path.expanduser(CONFIG_DIR), os.path.join(tempfolder, CONFIG_FOLDER))
|
|
tar = subprocess.Popen(['tar', 'czvf', backup_filename, '--dereference',
|
|
'-C', tempfolder, MANIFEST_NAME, CONFIG_FOLDER, DOWNLOAD_FOLDER])
|
|
tar.wait()
|
|
shutil.rmtree(tempfolder)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = OptionParser(usage='usage: %%prog [--create|--extract] <archive.gpo.tar.gz> [options]\n %%prog --purge\n\n%s' % __doc__.strip(),
|
|
version='%%prog %s' % __version__)
|
|
parser.add_option('-c', '--create',
|
|
dest='create', metavar='<FILE>',
|
|
help='Create a new archive')
|
|
parser.add_option('-x', '--extract',
|
|
dest='extract', metavar='<FILE>',
|
|
help='Extract an existing archive')
|
|
parser.add_option('-f', '--fake-downloads',
|
|
action='store_true', dest='fake', default=False,
|
|
help='Do not store contents of downloaded files')
|
|
parser.add_option('-n', '--no-covers',
|
|
action='store_false', dest='covers', default=True,
|
|
help='Do not include cover files in archive')
|
|
parser.add_option('-D', '--destination',
|
|
dest='destination', metavar='<DIR>',
|
|
help='Extract downloads in different folder')
|
|
parser.add_option('-P', '--purge',
|
|
action='store_true', dest='purge', default=False,
|
|
help='Remove current data (can be combined with --extract)')
|
|
|
|
(options, args) = parser.parse_args(sys.argv)
|
|
|
|
if options.create:
|
|
create_archive(options.create, options.fake, options.covers)
|
|
elif options.extract:
|
|
if options.purge:
|
|
do_purge()
|
|
extract_archive(options.extract, options.destination)
|
|
elif options.purge:
|
|
do_purge()
|
|
else:
|
|
parser.print_help()
|
|
|