2 # -*- coding: utf-8 -*-
3 # Module: LibraryExporter
4 # Created on: 13.01.2017
9 import cPickle as pickle
12 from utils import noop
15 """Exports Netflix shows & movies to a local library folder"""
17 series_label = 'shows'
18 """str: Label to identify shows"""
20 movies_label = 'movies'
21 """str: Label to identify movies"""
23 db_filename = 'lib.ndb'
24 """str: (File)Name of the store for the database dump that contains all shows/movies added to the library"""
26 def __init__ (self, root_folder, library_settings, log_fn=noop):
27 """Takes the instances & configuration options needed to drive the plugin
31 root_folder : :obj:`str`
34 library_settings : :obj:`str`
35 User data cache location
37 library_db_path : :obj:`str`
38 User data cache location
43 self.base_data_path = root_folder
44 self.enable_custom_library_folder = library_settings['enablelibraryfolder']
45 self.custom_library_folder = library_settings['customlibraryfolder']
46 self.db_filepath = os.path.join(self.base_data_path, self.db_filename)
49 # check for local library folder & set up the paths
50 lib_path = self.base_data_path if self.enable_custom_library_folder != 'true' else self.custom_library_folder
51 self.movie_path = os.path.join(lib_path, self.movies_label)
52 self.tvshow_path = os.path.join(lib_path, self.series_label)
54 # check if we need to setup the base folder structure & do so if needed
55 self.setup_local_netflix_library(source={
56 self.movies_label: self.movie_path,
57 self.series_label: self.tvshow_path
61 self.db = self._load_local_db(filename=self.db_filepath)
63 def setup_local_netflix_library (self, source):
64 """Sets up the basic directories
68 source : :obj:`dict` of :obj:`str`
69 Dicitionary with directories to be created
72 if not os.path.exists(source[label]):
73 os.makedirs(source[label])
75 def write_strm_file(self, path, url):
76 """Writes the stream file that Kodi can use to integrate it into the DB
81 Filepath of the file to be created
86 with open(path, 'w+') as f:
90 def _load_local_db (self, filename):
91 """Loads the local db file and parses it, creates one if not existent
101 Parsed contents of the db file
103 # if the db doesn't exist, create it
104 if not os.path.isfile(filename):
105 data = {self.movies_label: {}, self.series_label: {}}
106 self.log('Setup local library DB')
107 self._update_local_db(filename=filename, db=data)
110 with open(filename) as f:
111 data = pickle.load(f)
117 def _update_local_db (self, filename, db):
118 """Updates the local db file with new data
122 filename : :obj:`str`
131 Update has been successfully executed
133 if not os.path.isdir(os.path.dirname(filename)):
135 with open(filename, 'w') as f:
140 def movie_exists (self, title, year):
141 """Checks if a movie is already present in the local DB
149 Release year of the movie
156 movie_meta = '%s (%d)' % (title, year)
157 return movie_meta in self.db[self.movies_label]
159 def show_exists (self, title):
160 """Checks if a show is present in the local DB
172 show_meta = '%s' % (title)
173 return show_meta in self.db[self.series_label]
175 def season_exists (self, title, season):
176 """Checks if a season is present in the local DB
184 Season sequence number
189 Season of show exists in DB
191 if self.show_exists(title) == False:
193 show_entry = self.db[self.series_label][title]
194 return season in show_entry['seasons']
196 def episode_exists (self, title, season, episode):
197 """Checks if an episode if a show is present in the local DB
205 Season sequence number
208 Episode sequence number
213 Episode of show exists in DB
215 if self.show_exists(title) == False:
217 show_entry = self.db[self.series_label][title]
218 episode_entry = 'S%02dE%02d' % (season, episode)
219 return episode_entry in show_entry['episodes']
221 def add_movie (self, title, alt_title, year, video_id, build_url):
222 """Adds a movie to the local db, generates & persists the strm file
229 alt_title : :obj:`str`
230 Alternative title given by the user
233 Release year of the show
235 video_id : :obj:`str`
236 ID of the video to be played
238 build_url : :obj:`fn`
239 Function to generate the stream url
242 movie_meta = '%s (%d)' % (title, year)
244 dirname = os.path.join(self.movie_path, folder)
245 filename = os.path.join(dirname, movie_meta + '.strm')
246 if os.path.exists(filename):
248 if not os.path.exists(dirname):
250 if self.movie_exists(title=title, year=year) == False:
251 self.db[self.movies_label][movie_meta] = {'alt_title': alt_title}
252 self._update_local_db(filename=self.db_filepath, db=self.db)
253 self.write_strm_file(path=filename, url=build_url({'action': 'play_video', 'video_id': video_id}))
255 def add_show (self, title, alt_title, episodes, build_url):
256 """Adds a show to the local db, generates & persists the strm files
258 Note: Can also used to store complete seasons or single episodes, it all depends on
259 what is present in the episodes dictionary
266 alt_title : :obj:`str`
267 Alternative title given by the user
269 episodes : :obj:`dict` of :obj:`dict`
270 Episodes that need to be added
272 build_url : :obj:`fn`
273 Function to generate the stream url
275 show_meta = '%s' % (title)
277 show_dir = os.path.join(self.tvshow_path, folder)
278 if not os.path.exists(show_dir):
279 os.makedirs(show_dir)
280 if self.show_exists(title) == False:
281 self.db[self.series_label][show_meta] = {'seasons': [], 'episodes': [], 'alt_title': alt_title}
282 for episode in episodes:
283 self._add_episode(show_dir=show_dir, title=title, season=episode['season'], episode=episode['episode'], video_id=episode['id'], build_url=build_url)
284 self._update_local_db(filename=self.db_filepath, db=self.db)
287 def _add_episode (self, title, show_dir, season, episode, video_id, build_url):
288 """Adds a single episode to the local DB, generates & persists the strm file
295 show_dir : :obj:`str`
296 Directory that holds the stream files for that show
299 Season sequence number
302 Episode sequence number
304 video_id : :obj:`str`
305 ID of the video to be played
307 build_url : :obj:`fn`
308 Function to generate the stream url
311 episode = int(episode)
314 if self.season_exists(title=title, season=season) == False:
315 self.db[self.series_label][title]['seasons'].append(season)
318 episode_meta = 'S%02dE%02d' % (season, episode)
319 if self.episode_exists(title=title, season=season, episode=episode) == False:
320 self.db[self.series_label][title]['episodes'].append(episode_meta)
323 filename = episode_meta + '.strm'
324 filepath = os.path.join(show_dir, filename)
325 if os.path.exists(filepath):
327 self.write_strm_file(path=filepath, url=build_url({'action': 'play_video', 'video_id': video_id}))
329 def remove_movie (self, title, year):
330 """Removes the DB entry & the strm file for the movie given
338 Release year of the movie
345 movie_meta = '%s (%d)' % (title, year)
346 folder = self.db[self.movies_label][movie_meta]['alt_title']
347 del self.db[self.movies_label][movie_meta]
348 self._update_local_db(filename=self.db_filepath, db=self.db)
349 dirname = os.path.join(self.movie_path, folder)
350 if os.path.exists(dirname):
351 shutil.rmtree(dirname)
355 def remove_show (self, title):
356 """Removes the DB entry & the strm files for the show given
368 folder = self.db[self.series_label][title]['alt_title']
369 del self.db[self.series_label][title]
370 self._update_local_db(filename=self.db_filepath, db=self.db)
371 show_dir = os.path.join(self.tvshow_path, folder)
372 if os.path.exists(show_dir):
373 shutil.rmtree(show_dir)
377 def remove_season (self, title, season):
378 """Removes the DB entry & the strm files for a season of a show given
386 Season sequence number
396 show_meta = '%s' % (title)
397 for season_entry in self.db[self.series_label][show_meta]['seasons']:
398 if season_entry != season:
399 season_list.append(season_entry)
400 self.db[self.series_label][show_meta]['seasons'] = season_list
401 show_dir = os.path.join(self.tvshow_path, self.db[self.series_label][show_meta]['alt_title'])
402 if os.path.exists(show_dir):
403 show_files = [f for f in os.listdir(show_dir) if os.path.isfile(os.path.join(show_dir, f))]
404 for filename in show_files:
405 if 'S%02dE' % (season) in filename:
406 os.remove(os.path.join(show_dir, filename))
408 episodes_list.append(filename.replace('.strm', ''))
409 self.db[self.series_label][show_meta]['episodes'] = episodes_list
410 self._update_local_db(filename=self.db_filepath, db=self.db)
413 def remove_episode (self, title, season, episode):
414 """Removes the DB entry & the strm files for an episode of a show given
422 Season sequence number
425 Episode sequence number
433 show_meta = '%s' % (title)
434 episode_meta = 'S%02dE%02d' % (season, episode)
435 show_dir = os.path.join(self.tvshow_path, self.db[self.series_label][show_meta]['alt_title'])
436 if os.path.exists(os.path.join(show_dir, episode_meta + '.strm')):
437 os.remove(os.path.join(show_dir, episode_meta + '.strm'))
438 for episode_entry in self.db[self.series_label][show_meta]['episodes']:
439 if episode_meta != episode_entry:
440 episodes_list.append(episode_entry)
441 self.db[self.series_label][show_meta]['episodes'] = episodes_list
442 self._update_local_db(filename=self.db_filepath, db=self.db)