1 # -*- coding: utf-8 -*-
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2011 Thomas Perl and the gPodder Team
6 # gPodder is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # gPodder is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 from gpodder
import util
25 from gpodder
.sync
import (episode_filename_on_device
,
26 episode_foldername_on_device
)
28 import gi
# isort:skip
29 gi
.require_version('Gio', '2.0') # isort:skip
30 from gi
.repository
import Gio
, GLib
# isort:skip
35 logger
= logging
.getLogger(__name__
)
38 class gPodderDevicePlaylist(object):
39 def __init__(self
, config
, playlist_name
):
41 self
.linebreak
= '\r\n'
42 self
.playlist_file
= (
43 util
.sanitize_filename(playlist_name
, self
._config
.device_sync
.max_filename_length
)
44 + '.' + self
._config
.device_sync
.playlists
.extension
)
45 device_folder
= util
.new_gio_file(self
._config
.device_sync
.device_folder
)
46 self
.playlist_folder
= device_folder
.resolve_relative_path(self
._config
.device_sync
.playlists
.folder
)
48 self
.mountpoint
= None
50 self
.mountpoint
= self
.playlist_folder
.find_enclosing_mount().get_root()
51 except GLib
.Error
as err
:
52 logger
.error('find_enclosing_mount folder %s failed: %s', self
.playlist_folder
.get_uri(), err
.message
)
54 if not self
.mountpoint
:
55 self
.mountpoint
= self
.playlist_folder
56 logger
.warning('could not find mount point for MP3 player - using %s as MP3 player root', self
.mountpoint
.get_uri())
57 self
.playlist_absolute_filename
= self
.playlist_folder
.resolve_relative_path(self
.playlist_file
)
59 def build_extinf(self
, filename
):
60 # TODO: Windows playlists
61 # if self._config.mp3_player_playlist_win_path:
62 # filename = filename.replace('\\', os.sep)
64 # # rebuild the whole filename including the mountpoint
65 # if self._config.device_sync.playlist_absolute_path:
66 # absfile = os.path.join(self.mountpoint,filename)
67 # else: #TODO: Test rel filenames
68 # absfile = util.rel2abs(filename, os.path.dirname(self.playlist_file))
70 # fallback: use the basename of the file
71 (title
, extension
) = os
.path
.splitext(os
.path
.basename(filename
))
73 return "#EXTINF:0,%s%s" % (title
.strip(), self
.linebreak
)
77 read all files from the existing playlist
80 logger
.info("Read data from the playlistfile %s" % self
.playlist_absolute_filename
.get_uri())
81 if self
.playlist_absolute_filename
.query_exists():
82 stream
= Gio
.DataInputStream
.new(self
.playlist_absolute_filename
.read())
84 line
= stream
.read_line_utf8()[0]
87 if not line
.startswith('#EXT'):
88 tracks
.append(line
.rstrip('\r\n'))
92 def get_filename_for_playlist(self
, episode
):
94 get the filename for the given episode for the playlist
96 return episode_filename_on_device(self
._config
, episode
)
98 def get_absolute_filename_for_playlist(self
, episode
):
100 get the filename including full path for the given episode for the playlist
102 filename
= self
.get_filename_for_playlist(episode
)
103 foldername
= episode_foldername_on_device(self
._config
, episode
)
105 filename
= os
.path
.join(foldername
, filename
)
106 if self
._config
.device_sync
.playlists
.use_absolute_path
:
107 filename
= os
.path
.join(util
.relpath(self
._config
.device_sync
.device_folder
, self
.mountpoint
.get_uri()), filename
)
110 def write_m3u(self
, episodes
):
112 write the list into the playlist on the device
114 logger
.info('Writing playlist file: %s', self
.playlist_file
)
115 if not util
.make_directory(self
.playlist_folder
):
116 raise IOError(_('Folder %s could not be created.') % self
.playlist_folder
, _('Error writing playlist'))
118 # work around libmtp devices potentially having limited capabilities for partial writes
119 is_mtp
= self
.playlist_folder
.get_uri().startswith("mtp://")
122 tempfile
= Gio
.File
.new_tmp()
123 fs
= tempfile
[1].get_output_stream()
125 fs
= self
.playlist_absolute_filename
.replace(None, False, Gio
.FileCreateFlags
.NONE
)
127 os
= Gio
.DataOutputStream
.new(fs
)
128 os
.put_string('#EXTM3U%s' % self
.linebreak
)
129 for current_episode
in episodes
:
130 filename
= self
.get_filename_for_playlist(current_episode
)
131 os
.put_string(self
.build_extinf(filename
))
132 filename
= self
.get_absolute_filename_for_playlist(current_episode
)
133 os
.put_string(filename
)
134 os
.put_string(self
.linebreak
)
139 tempfile
[0].copy(self
.playlist_absolute_filename
, Gio
.FileCopyFlags
.OVERWRITE
)
140 except GLib
.Error
as err
:
141 logger
.error('copying playlist to mtp device file %s failed: %s',
142 self
.playlist_absolute_filename
.get_uri(), err
.message
)