fix #494 - ensure short filenames
- fix rename_download that was causing #494 - max_length is now mandatory in util.sanitize_filename - add max_length param where it's missing - factor name computation in deviceplaylist and sync
This commit is contained in:
parent
e7e5b2209e
commit
66bc2a6f72
|
@ -7,6 +7,7 @@ import os
|
|||
|
||||
import gpodder
|
||||
from gpodder import util
|
||||
from gpodder.model import PodcastEpisode
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -46,18 +47,21 @@ class gPodderExtension:
|
|||
dirname = os.path.dirname(current_filename)
|
||||
filename = os.path.basename(current_filename)
|
||||
basename, ext = os.path.splitext(filename)
|
||||
ext = utils.sanitize_filename(ext, PodcastEpisode.MAX_FILENAME_LENGTH)
|
||||
|
||||
new_basename = []
|
||||
new_basename.append(title + ext)
|
||||
new_basename.append(title)
|
||||
if self.config.add_podcast_title:
|
||||
new_basename.insert(0, podcast_title)
|
||||
if self.config.add_sortdate:
|
||||
new_basename.insert(0, sortdate)
|
||||
new_basename = ' - '.join(new_basename)
|
||||
|
||||
# On Windows, force ASCII encoding for filenames (bug 1724)
|
||||
new_basename = util.sanitize_filename(new_basename)
|
||||
new_filename = os.path.join(dirname, new_basename)
|
||||
# Remove unwanted characters and shorten filename (#494)
|
||||
new_basename = util.sanitize_filename(new_basename, PodcastEpisode.MAX_FILENAME_LENGTH)
|
||||
# add extension after sanitization, to keep it even if filename is longer than limit
|
||||
# (it's unlikely that new_basename + ext is longer than is allowed on platform).
|
||||
new_filename = os.path.join(dirname, new_basename + ext)
|
||||
|
||||
if new_filename == current_filename:
|
||||
return current_filename
|
||||
|
|
|
@ -162,7 +162,7 @@ defaults = {
|
|||
'skip_played_episodes': True,
|
||||
'delete_played_episodes': False,
|
||||
|
||||
'max_filename_length': 999,
|
||||
'max_filename_length': 120,
|
||||
|
||||
'custom_sync_name': '{episode.sortdate}_{episode.title}',
|
||||
'custom_sync_name_enabled': False,
|
||||
|
|
|
@ -23,6 +23,7 @@ import gpodder
|
|||
_ = gpodder.gettext
|
||||
|
||||
from gpodder import util
|
||||
from gpodder.sync import episode_filename_on_device, episode_foldername_on_device
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -32,7 +33,7 @@ class gPodderDevicePlaylist(object):
|
|||
def __init__(self, config, playlist_name):
|
||||
self._config = config
|
||||
self.linebreak = '\r\n'
|
||||
self.playlist_file = util.sanitize_filename(playlist_name + '.m3u')
|
||||
self.playlist_file = util.sanitize_filename(playlist_name, self._config.device_sync.max_filename_length) + '.m3u'
|
||||
self.playlist_folder = os.path.join(self._config.device_sync.device_folder, self._config.device_sync.playlists.folder)
|
||||
self.mountpoint = util.find_mount_point(self.playlist_folder)
|
||||
if self.mountpoint == '/':
|
||||
|
@ -72,20 +73,16 @@ class gPodderDevicePlaylist(object):
|
|||
"""
|
||||
get the filename for the given episode for the playlist
|
||||
"""
|
||||
filename_base = util.sanitize_filename(episode.sync_filename(
|
||||
self._config.device_sync.custom_sync_name_enabled,
|
||||
self._config.device_sync.custom_sync_name),
|
||||
self._config.device_sync.max_filename_length)
|
||||
filename = filename_base + os.path.splitext(episode.local_filename(create=False))[1].lower()
|
||||
return filename
|
||||
return episode_filename_on_device(self._config, episode)
|
||||
|
||||
def get_absolute_filename_for_playlist(self, episode):
|
||||
"""
|
||||
get the filename including full path for the given episode for the playlist
|
||||
"""
|
||||
filename = self.get_filename_for_playlist(episode)
|
||||
if self._config.device_sync.one_folder_per_podcast:
|
||||
filename = os.path.join(util.sanitize_filename(episode.channel.title), filename)
|
||||
foldername = episode_foldername_on_device(self._config, episode)
|
||||
if foldername:
|
||||
filename = os.path.join(foldername, filename)
|
||||
if self._config.device_sync.playlist.absolute_path:
|
||||
filename = os.path.join(util.relpath(self.mountpoint, self._config.device_sync.device_folder), filename)
|
||||
return filename
|
||||
|
|
|
@ -60,6 +60,7 @@ from gpodder import my
|
|||
from gpodder import youtube
|
||||
from gpodder import player
|
||||
from gpodder import common
|
||||
from gpodder.model import PodcastEpisode
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -1772,7 +1773,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
|
||||
@staticmethod
|
||||
def build_filename(filename, extension):
|
||||
filename = util.sanitize_filename(filename)
|
||||
filename = util.sanitize_filename(filename, PodcastEpisode.MAX_FILENAME_LENGTH)
|
||||
if not filename.endswith(extension):
|
||||
filename += extension
|
||||
return filename
|
||||
|
|
|
@ -145,6 +145,46 @@ def get_track_length(filename):
|
|||
# Default is three hours (to be on the safe side)
|
||||
|
||||
|
||||
def episode_filename_on_device(config, episode):
|
||||
"""
|
||||
:param gpodder.config.Config config: configuration (for sync options)
|
||||
:param gpodder.model.PodcastEpisode episode: episode to get filename for
|
||||
:return str: basename minus extension to use to save episode on device
|
||||
"""
|
||||
# get the local file
|
||||
from_file = episode.local_filename(create=False)
|
||||
# get the formated base name
|
||||
filename_base = util.sanitize_filename(episode.sync_filename(
|
||||
config.device_sync.custom_sync_name_enabled,
|
||||
config.device_sync.custom_sync_name),
|
||||
config.device_sync.max_filename_length)
|
||||
# add the file extension
|
||||
to_file = filename_base + os.path.splitext(from_file)[1].lower()
|
||||
|
||||
# dirty workaround: on bad (empty) episode titles,
|
||||
# we simply use the from_file basename
|
||||
# (please, podcast authors, FIX YOUR RSS FEEDS!)
|
||||
if os.path.splitext(to_file)[0] == '':
|
||||
to_file = os.path.basename(from_file)
|
||||
return to_file
|
||||
|
||||
|
||||
def episode_foldername_on_device(config, episode):
|
||||
"""
|
||||
:param gpodder.config.Config config: configuration (for sync options)
|
||||
:param gpodder.model.PodcastEpisode episode: episode to get folder name for
|
||||
:return str: folder name to save episode to on device
|
||||
"""
|
||||
if config.device_sync.one_folder_per_podcast:
|
||||
# Add channel title as subfolder
|
||||
folder = episode.channel.title
|
||||
# Clean up the folder name for use on limited devices
|
||||
folder = util.sanitize_filename(folder, config.device_sync.max_filename_length)
|
||||
else:
|
||||
folder = None
|
||||
return folder
|
||||
|
||||
|
||||
class SyncTrack(object):
|
||||
"""
|
||||
This represents a track that is on a device. You need
|
||||
|
@ -527,12 +567,8 @@ class MP3PlayerDevice(Device):
|
|||
return False
|
||||
|
||||
def get_episode_folder_on_device(self, episode):
|
||||
if self._config.device_sync.one_folder_per_podcast:
|
||||
# Add channel title as subfolder
|
||||
folder = episode.channel.title
|
||||
# Clean up the folder name for use on limited devices
|
||||
folder = util.sanitize_filename(folder,
|
||||
self._config.device_sync.max_filename_length)
|
||||
folder = episode_foldername_on_device(self._config, episode)
|
||||
if folder:
|
||||
folder = os.path.join(self.destination, folder)
|
||||
else:
|
||||
folder = self.destination
|
||||
|
@ -540,23 +576,7 @@ class MP3PlayerDevice(Device):
|
|||
return folder
|
||||
|
||||
def get_episode_file_on_device(self, episode):
|
||||
# get the local file
|
||||
from_file = episode.local_filename(create=False)
|
||||
# get the formated base name
|
||||
filename_base = util.sanitize_filename(episode.sync_filename(
|
||||
self._config.device_sync.custom_sync_name_enabled,
|
||||
self._config.device_sync.custom_sync_name),
|
||||
self._config.device_sync.max_filename_length)
|
||||
# add the file extension
|
||||
to_file = filename_base + os.path.splitext(from_file)[1].lower()
|
||||
|
||||
# dirty workaround: on bad (empty) episode titles,
|
||||
# we simply use the from_file basename
|
||||
# (please, podcast authors, FIX YOUR RSS FEEDS!)
|
||||
if os.path.splitext(to_file)[0] == '':
|
||||
to_file = os.path.basename(from_file)
|
||||
|
||||
return to_file
|
||||
return episode_filename_on_device(self._config, episode)
|
||||
|
||||
def add_track(self, episode,reporthook=None):
|
||||
self.notify('status', _('Adding %s') % episode.title)
|
||||
|
|
|
@ -1468,16 +1468,16 @@ def convert_bytes(d):
|
|||
return d
|
||||
|
||||
|
||||
def sanitize_filename(filename, max_length=0):
|
||||
def sanitize_filename(filename, max_length):
|
||||
"""
|
||||
Generate a sanitized version of a filename; trim filename
|
||||
if greater than max_length (0 = no limit).
|
||||
|
||||
>>> sanitize_filename('https://www.host.name/feed')
|
||||
>>> sanitize_filename('https://www.host.name/feed', 0)
|
||||
'https___www.host.name_feed'
|
||||
>>> sanitize_filename('Binärgewitter')
|
||||
>>> sanitize_filename('Binärgewitter', 0)
|
||||
'Binärgewitter'
|
||||
>>> sanitize_filename('Cool feed (ogg)')
|
||||
>>> sanitize_filename('Cool feed (ogg)', 0)
|
||||
'Cool feed (ogg)'
|
||||
>>> sanitize_filename('Cool feed (ogg)', 1)
|
||||
'C'
|
||||
|
|
Loading…
Reference in New Issue