1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# gPodder - A media aggregator and podcast client
# Copyright (c) 2005-2012 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()
|