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, pin, 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
241 build_url : :obj:`fn`
242 Function to generate the stream url
245 movie_meta = '%s (%d)' % (title, year)
247 dirname = os.path.join(self.movie_path, folder)
248 filename = os.path.join(dirname, movie_meta + '.strm')
249 if os.path.exists(filename):
251 if not os.path.exists(dirname):
253 if self.movie_exists(title=title, year=year) == False:
254 self.db[self.movies_label][movie_meta] = {'alt_title': alt_title}
255 self._update_local_db(filename=self.db_filepath, db=self.db)
256 self.write_strm_file(path=filename, url=build_url({'action': 'play_video', 'video_id': video_id, 'pin': pin}))
258 def add_show (self, title, alt_title, episodes, build_url):
259 """Adds a show to the local db, generates & persists the strm files
261 Note: Can also used to store complete seasons or single episodes, it all depends on
262 what is present in the episodes dictionary
269 alt_title : :obj:`str`
270 Alternative title given by the user
272 episodes : :obj:`dict` of :obj:`dict`
273 Episodes that need to be added
275 build_url : :obj:`fn`
276 Function to generate the stream url
278 show_meta = '%s' % (title)
280 show_dir = os.path.join(self.tvshow_path, folder)
281 if not os.path.exists(show_dir):
282 os.makedirs(show_dir)
283 if self.show_exists(title) == False:
284 self.db[self.series_label][show_meta] = {'seasons': [], 'episodes': [], 'alt_title': alt_title}
285 for episode in episodes:
286 self._add_episode(show_dir=show_dir, title=title, season=episode['season'], episode=episode['episode'], video_id=episode['id'], pin=episode['pin'], build_url=build_url)
287 self._update_local_db(filename=self.db_filepath, db=self.db)
290 def _add_episode (self, title, show_dir, season, episode, video_id, pin, build_url):
291 """Adds a single episode to the local DB, generates & persists the strm file
298 show_dir : :obj:`str`
299 Directory that holds the stream files for that show
302 Season sequence number
305 Episode sequence number
307 video_id : :obj:`str`
308 ID of the video to be played
313 build_url : :obj:`fn`
314 Function to generate the stream url
317 episode = int(episode)
320 if self.season_exists(title=title, season=season) == False:
321 self.db[self.series_label][title]['seasons'].append(season)
324 episode_meta = 'S%02dE%02d' % (season, episode)
325 if self.episode_exists(title=title, season=season, episode=episode) == False:
326 self.db[self.series_label][title]['episodes'].append(episode_meta)
329 filename = episode_meta + '.strm'
330 filepath = os.path.join(show_dir, filename)
331 if os.path.exists(filepath):
333 self.write_strm_file(path=filepath, url=build_url({'action': 'play_video', 'video_id': video_id, 'pin': pin}))
335 def remove_movie (self, title, year):
336 """Removes the DB entry & the strm file for the movie given
344 Release year of the movie
351 movie_meta = '%s (%d)' % (title, year)
352 folder = self.db[self.movies_label][movie_meta]['alt_title']
353 del self.db[self.movies_label][movie_meta]
354 self._update_local_db(filename=self.db_filepath, db=self.db)
355 dirname = os.path.join(self.movie_path, folder)
356 if os.path.exists(dirname):
357 shutil.rmtree(dirname)
361 def remove_show (self, title):
362 """Removes the DB entry & the strm files for the show given
374 folder = self.db[self.series_label][title]['alt_title']
375 del self.db[self.series_label][title]
376 self._update_local_db(filename=self.db_filepath, db=self.db)
377 show_dir = os.path.join(self.tvshow_path, folder)
378 if os.path.exists(show_dir):
379 shutil.rmtree(show_dir)
383 def remove_season (self, title, season):
384 """Removes the DB entry & the strm files for a season of a show given
392 Season sequence number
402 show_meta = '%s' % (title)
403 for season_entry in self.db[self.series_label][show_meta]['seasons']:
404 if season_entry != season:
405 season_list.append(season_entry)
406 self.db[self.series_label][show_meta]['seasons'] = season_list
407 show_dir = os.path.join(self.tvshow_path, self.db[self.series_label][show_meta]['alt_title'])
408 if os.path.exists(show_dir):
409 show_files = [f for f in os.listdir(show_dir) if os.path.isfile(os.path.join(show_dir, f))]
410 for filename in show_files:
411 if 'S%02dE' % (season) in filename:
412 os.remove(os.path.join(show_dir, filename))
414 episodes_list.append(filename.replace('.strm', ''))
415 self.db[self.series_label][show_meta]['episodes'] = episodes_list
416 self._update_local_db(filename=self.db_filepath, db=self.db)
419 def remove_episode (self, title, season, episode):
420 """Removes the DB entry & the strm files for an episode of a show given
428 Season sequence number
431 Episode sequence number
439 show_meta = '%s' % (title)
440 episode_meta = 'S%02dE%02d' % (season, episode)
441 show_dir = os.path.join(self.tvshow_path, self.db[self.series_label][show_meta]['alt_title'])
442 if os.path.exists(os.path.join(show_dir, episode_meta + '.strm')):
443 os.remove(os.path.join(show_dir, episode_meta + '.strm'))
444 for episode_entry in self.db[self.series_label][show_meta]['episodes']:
445 if episode_meta != episode_entry:
446 episodes_list.append(episode_entry)
447 self.db[self.series_label][show_meta]['episodes'] = episodes_list
448 self._update_local_db(filename=self.db_filepath, db=self.db)