def add_movies(self, url, movie_list=[], movie_ids=[], max_age=0): """ add movies to list """ max_date = add_years(max_age * -1) print(u"Retrieving the trakt list: {}".format(url)) data = {} if max_age != 0: data['extended'] = 'full' movie_data = self._handle_request('get', url, data=data) for meta in movie_data: if 'movie' not in meta: meta['movie'] = meta # Skip already added movies if meta['movie']['ids']['imdb'] in movie_ids: continue if not meta['movie']['year']: continue # Skip old movies if max_age != 0 \ and (max_date > datetime.datetime.strptime( meta['movie']['released'], '%Y-%m-%d')): continue movie_list.append({ 'id': meta['movie']['ids']['imdb'], 'tmdb_id': meta['movie']['ids'].get('tmdb', ''), 'title': meta['movie']['title'], 'year': meta['movie']['year'], }) movie_ids.append(meta['movie']['ids']['imdb']) if meta['movie']['ids'].get('tmdb'): movie_ids.append('tmdb' + str(meta['movie']['ids']['tmdb'])) return movie_list, movie_ids
def add_movies(self, url, movie_list=None, movie_ids=None, max_age=0): if not movie_list: movie_list = [] if not movie_ids: movie_ids = [] max_date = add_years(max_age * -1) logs.info(u"Retrieving the trakt list: {}".format(url)) data = {} if max_age != 0: data['extended'] = 'full' movie_data = self._handle_request('get', url, data=data) for m in movie_data: if 'movie' not in m: m['movie'] = m # Skip already added movies if m['movie']['ids']['imdb'] in movie_ids: continue if not m['movie']['year']: # TODO: Handle this better? continue # Skip old movies if max_age != 0 \ and (max_date > datetime.datetime.strptime( m['movie']['released'], '%Y-%m-%d')): continue movie_list.append({ 'id': m['movie']['ids']['imdb'], 'tmdb_id': str(m['movie']['ids'].get('tmdb', '')), 'title': m['movie']['title'], 'year': m['movie']['year'], }) movie_ids.append(m['movie']['ids']['imdb']) if m['movie']['ids'].get('tmdb'): movie_ids.append('tmdb' + str(m['movie']['ids']['tmdb'])) return movie_list, movie_ids
def _extend_over_planning_horizon(self, selected_days, extracted_series, extracted_weights): """ :param selected_days: output of reprensentative days selection :param extracted_series: output of reprensentative days selection :param extracted_weights: output of reprensentative days selection :return: 3 lists with selected_days, extracted_series, and extracted_weights extended to the investment horizon with progressions applied if any. """ logging.info( 'Extending representative days of first year over investment horizon with progressions...' ) # Simply replicate days and weights with a time shift of one year days = [{add_years(k, i): v for k, v in selected_days[0].items()} for i in range(self.sizing_config.investment_horizon)] weights = [{ add_years(k, i): v for k, v in extracted_weights[0].items() } for i in range(self.sizing_config.investment_horizon)] # Extend series to investment horizon init_series = extracted_series[0].copy() series = [init_series] current_series = init_series.copy() # Fetch progressions first, if any. progressions = self._get_progressions() for i in range(1, self.sizing_config.investment_horizon): # Compute number of days of the year (some have 366 ...) current_year = current_series.index[0].year one_year_delta = (datetime(current_year + 1, 1, 1) - datetime(current_year, 1, 1)) new_series = current_series.copy() # Add a year new_series.index += one_year_delta # apply progressions for device_name, progression in progressions.items(): new_series[device_name] *= (1 + progression) series.append(new_series) current_series = new_series return days, series, weights
def __determine_season(self): """Attempt to determine the season from `start` and `end`. Note: all we care about is the years. Returns: {'start': datetime, 'end': datetime}: Any date in the starting year of the season followed by `self.end` """ if self.start.year == self.end.year: start = utils.add_years(self.start, -1) else: start = self.start return {'start': start, 'end': self.end}
def process_cakeday_message(message, database): log.info("Processing cakeday") if database.get_cakeday(message.author.name) is not None: log.info("Cakeday already exists") return ["It looks like you already have a cakeday reminder set."] account_created = utils.datetime_from_timestamp(message.author.created_utc) next_anniversary = utils.add_years( account_created, utils.datetime_now().year - account_created.year) if next_anniversary < utils.datetime_now(): next_anniversary = utils.add_years(next_anniversary, 1) log.debug( f"u/{message.author.name} created {utils.get_datetime_string(account_created)}, " f"anniversary {utils.get_datetime_string(next_anniversary)}") cakeday = Cakeday(message.author.name, next_anniversary) database.add_cakeday(cakeday) return cakeday.render_confirmation( database.get_settings(message.author.name).timezone)
class ExportResultsForm(FlaskForm): body = SelectField(u'Jednotka', choices=filter(lambda o: o[0][:4] == "body", UNITS)) membertype = SelectField(u'Funkce', choices=[('Member', u'Člen'), ('Coordinator', u'Koordinátor'), ('Vicepresident', u'Místopředseda'), ('President', u'Předseda')]) since = DateField(u'Od', format='%Y-%m-%d', default=datetime.datetime.now()) till = DateField(u'Do', format='%Y-%m-%d', default=add_years(datetime.datetime.now(), 2))
def add_movies(self, url, movie_list=None, movie_ids=None, max_age=0): if not movie_list: movie_list = [] if not movie_ids: movie_ids = [] max_date = add_years(max_age * -1) logs.info(u"Retrieving the IMDB list: {}".format(url)) (imdb_ids, imdb_titles, imdb_years) = self._handle_request(url) for i, imdb_id in enumerate(imdb_ids): # Skip already added movies if imdb_id in movie_ids: continue if self.tmdb: tmdb_data = self.tmdb.get_tmdb_from_imdb(imdb_id, 'movie') if tmdb_data and tmdb_data['release_date']: date = datetime.datetime.strptime(tmdb_data['release_date'], '%Y-%m-%d') elif imdb_years[i]: date = datetime.datetime(int(str(imdb_years[i]).strip("()")), 12, 31) else: date = datetime.date.today() # Skip old movies if max_age != 0 and (max_date > date): continue movie_list.append({ 'id': imdb_id, 'tmdb_id': tmdb_data['id'] if tmdb_data else None, 'title': tmdb_data['title'] if tmdb_data else imdb_titles[i], 'year': date.year, }) movie_ids.append(imdb_id) if tmdb_data and tmdb_data['id']: movie_ids.append('tmdb' + str(tmdb_data['id'])) return movie_list, movie_ids
def bump_cakeday(self, cakeday): if cakeday.db_id is None: log.warning(f"This cakeday doesn't exist: {cakeday.user}") c = self.dbConn.cursor() log.debug("Bumping cakeday one year") try: c.execute( ''' UPDATE cakedays SET CakedayDate = ? WHERE ID = ? ''', (utils.get_datetime_string(utils.add_years(cakeday.date_time, 1)), cakeday.db_id)) except sqlite3.IntegrityError as err: log.warning(f"Failed to bump cakeday: {err}") return False self.dbConn.commit() return True
def _remove_old_items_from_library(self, unmatched_items): logs.info(u"Removing symlinks for items " "which no longer qualify ".format( library=self.recipe['new_library']['name'])) count = 0 updated_paths = [] deleted_items = [] max_date = add_years((self.recipe['new_library']['max_age'] or 0) * -1) if self.library_type == 'movie': for movie in unmatched_items: if not self.recipe['new_library']['remove_from_library']: # Only remove older than max_age if not self.recipe['new_library']['max_age'] \ or (movie.originallyAvailableAt and max_date < movie.originallyAvailableAt): continue for part in movie.iterParts(): old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = os.path.relpath( old_path, self.recipe['new_library']['folder']) if folder_name == '.': new_path = os.path.join( self.recipe['new_library']['folder'], file_name) dir = False else: new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) dir = True if (dir and os.path.exists(new_path)) or ( not dir and os.path.isfile(new_path)): try: if os.name == 'nt': # Python 3.2+ only if sys.version_info < (3, 2): assert os.path.islink(new_path) if dir: os.rmdir(new_path) else: os.remove(new_path) else: assert os.path.islink(new_path) os.unlink(new_path) count += 1 deleted_items.append(movie) updated_paths.append(new_path) except Exception as e: logs.error(u"Remove symlink failed for " "{path}: {e}".format(path=new_path, e=e)) else: for tv_show in unmatched_items: done = False if done: continue for episode in tv_show.episodes(): if done: break for part in episode.iterParts(): if done: break old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = '' new_library_folder = \ self.recipe['new_library']['folder'] old_path = os.path.join( new_library_folder, old_path.replace(new_library_folder, '').strip( os.sep).split(os.sep)[0]) folder_name = os.path.relpath(old_path, new_library_folder) new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) if os.path.exists(new_path): try: if os.name == 'nt': # Python 3.2+ only if sys.version_info < (3, 2): assert os.path.islink(new_path) os.rmdir(new_path) else: assert os.path.islink(new_path) os.unlink(new_path) count += 1 deleted_items.append(tv_show) updated_paths.append(new_path) done = True break except Exception as e: logs.error(u"Remove symlink failed for " "{path}: {e}".format(path=new_path, e=e)) else: done = True break logs.info(u"Removed symlinks for {count} items.".format(count=count)) for item in deleted_items: logs.info(u"{title} ({year})".format(title=item.title, year=item.year))
def _run(self): item_list = [] # TODO Replace with dict, scrap item_ids? item_ids = [] force_imdb_id_match = False # Get the trakt lists for url in self.recipe['source_list_urls']: if 'api.trakt.tv' in url: (item_list, item_ids) = self.trakt.add_items( self.library_type, url, item_list, item_ids, self.recipe['new_library']['max_age'] or 0) elif 'imdb.com/chart' in url: (item_list, item_ids) = self.imdb.add_items( self.library_type, url, item_list, item_ids, self.recipe['new_library']['max_age'] or 0) else: raise Exception( "Unsupported source list: {url}".format(url=url)) if self.recipe['weighted_sorting']['enabled']: if self.config['tmdb']['api_key']: print(u"Getting data from TMDb to add weighted sorting...") item_list = self.weighted_sorting(item_list) else: print(u"Warning: TMDd API key is required " u"for weighted sorting") # Get list of items from the Plex server source_libraries = [] for library_config in self.source_library_config: print(u"Trying to match with items from the '{}' library ".format( library_config['name'])) try: source_library = self.plex.server.library.section( library_config['name']) except: # FIXME raise Exception("The '{}' library does not exist".format( library_config['name'])) # FIXME: Hack until a new plexapi version is released. 3.0.4? if 'guid' not in source_library.ALLOWED_FILTERS: source_library.ALLOWED_FILTERS += ('guid', ) source_libraries.append(source_library) # Create a list of matching items matching_items = [] missing_items = [] matching_total = 0 nonmatching_idx = [] max_count = self.recipe['new_library']['max_count'] for i, item in enumerate(item_list): match = False if max_count > 0 and matching_total >= max_count: nonmatching_idx.append(i) continue res = [] for source_library in source_libraries: lres = source_library.search(guid='imdb://' + str(item['id'])) if not lres and item.get('tmdb_id'): lres += source_library.search(guid='themoviedb://' + str(item['tmdb_id'])) if not lres and item.get('tvdb_id'): lres += source_library.search(guid='thetvdb://' + str(item['tvdb_id'])) if lres: res += lres if not res: missing_items.append((i, item)) nonmatching_idx.append(i) continue for r in res: imdb_id = None tmdb_id = None tvdb_id = None if r.guid is not None and 'imdb://' in r.guid: imdb_id = r.guid.split('imdb://')[1].split('?')[0] elif r.guid is not None and 'themoviedb://' in r.guid: tmdb_id = r.guid.split('themoviedb://')[1].split('?')[0] elif r.guid is not None and 'thetvdb://' in r.guid: tvdb_id = (r.guid.split('thetvdb://')[1].split('?') [0].split('/')[0]) if ((imdb_id and imdb_id == str(item['id'])) or (tmdb_id and tmdb_id == str(item['tmdb_id'])) or (tvdb_id and tvdb_id == str(item['tvdb_id']))): if not match: match = True matching_total += 1 matching_items.append(r) if match: if self.recipe['new_library']['sort_title']['absolute']: print(u"{} {} ({})".format(i + 1, item['title'], item['year'])) else: print(u"{} {} ({})".format(matching_total, item['title'], item['year'])) else: missing_items.append((i, item)) nonmatching_idx.append(i) if not self.recipe['new_library']['sort_title']['absolute']: for i in reversed(nonmatching_idx): del item_list[i] # Create symlinks for all items in your library on the trakt watched print(u"Creating symlinks for {count} matching items in the " u"library...".format(count=matching_total)) try: if not os.path.exists(self.recipe['new_library']['folder']): os.mkdir(self.recipe['new_library']['folder']) except: print(u"Unable to create the new library folder " u"'{folder}'.".format( folder=self.recipe['new_library']['folder'])) print(u"Exiting script.") return 0 count = 0 updated_paths = [] new_items = [] if self.library_type == 'movie': for movie in matching_items: for part in movie.iterParts(): old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = '' for library_config in self.source_library_config: for f in library_config['folders']: f = os.path.abspath(f) if old_path.lower().startswith(f.lower()): folder_name = os.path.relpath(old_path, f) break else: continue if folder_name == '.': new_path = os.path.join( self.recipe['new_library']['folder'], file_name) dir = False else: new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) dir = True parent_path = os.path.dirname( os.path.abspath(new_path)) if not os.path.exists(parent_path): try: os.makedirs(parent_path) except OSError as e: if e.errno == errno.EEXIST \ and os.path.isdir(parent_path): pass else: raise # Clean up old, empty directories if os.path.exists(new_path) \ and not os.listdir(new_path): os.rmdir(new_path) if (dir and not os.path.exists(new_path)) \ or not dir and not os.path.isfile(new_path): try: if os.name == 'nt': if dir: subprocess.call([ 'mklink', '/D', new_path, old_path ], shell=True) else: subprocess.call([ 'mklink', new_path, old_path_file ], shell=True) else: if dir: os.symlink(old_path, new_path) else: os.symlink(old_path_file, new_path) count += 1 new_items.append(movie) updated_paths.append(new_path) except Exception as e: print(u"Symlink failed for {path}: {e}".format( path=new_path, e=e)) else: for tv_show in matching_items: done = False if done: continue for episode in tv_show.episodes(): if done: break for part in episode.iterParts(): old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = '' for library_config in self.source_library_config: for f in library_config['folders']: if old_path.lower().startswith(f.lower()): old_path = os.path.join( f, old_path.replace(f, '').strip( os.sep).split(os.sep)[0]) folder_name = os.path.relpath(old_path, f) break else: continue new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) if not os.path.exists(new_path): try: if os.name == 'nt': subprocess.call([ 'mklink', '/D', new_path, old_path ], shell=True) else: os.symlink(old_path, new_path) count += 1 new_items.append(tv_show) updated_paths.append(new_path) done = True break except Exception as e: print(u"Symlink failed for {path}: {e}". format(path=new_path, e=e)) else: done = True break print(u"Created symlinks for {count} new items:".format(count=count)) for item in new_items: print(u"{title} ({year})".format(title=item.title, year=item.year)) # Check if the new library exists in Plex print(u"Creating the '{}' library in Plex...".format( self.recipe['new_library']['name'])) try: new_library = self.plex.server.library.section( self.recipe['new_library']['name']) print(u"Library already exists in Plex. Scanning the library...") new_library.update() except plexapi.exceptions.NotFound: self.plex.create_new_library(self.recipe['new_library']['name'], self.recipe['new_library']['folder'], self.library_type) new_library = self.plex.server.library.section( self.recipe['new_library']['name']) # Wait for metadata to finish downloading before continuing print(u"Waiting for metadata to finish downloading...") new_library = self.plex.server.library.section( self.recipe['new_library']['name']) while new_library.refreshing: time.sleep(5) new_library = self.plex.server.library.section( self.recipe['new_library']['name']) # Retrieve a list of items from the new library print(u"Retrieving a list of items from the '{library}' library in " u"Plex...".format(library=self.recipe['new_library']['name'])) all_new_items = new_library.all() # Create a dictionary of {imdb_id: item} imdb_map = {} for m in all_new_items: imdb_id = None tmdb_id = None tvdb_id = None if m.guid is not None and 'imdb://' in m.guid: imdb_id = m.guid.split('imdb://')[1].split('?')[0] elif m.guid is not None and 'themoviedb://' in m.guid: tmdb_id = m.guid.split('themoviedb://')[1].split('?')[0] elif m.guid is not None and 'thetvdb://' in m.guid: tvdb_id = ( m.guid.split('thetvdb://')[1].split('?')[0].split('/')[0]) else: imdb_id = None if imdb_id and str(imdb_id) in item_ids: imdb_map[imdb_id] = m elif tmdb_id and ('tmdb' + str(tmdb_id)) in item_ids: imdb_map['tmdb' + str(tmdb_id)] = m elif tvdb_id and ('tvdb' + str(tvdb_id)) in item_ids: imdb_map['tvdb' + str(tvdb_id)] = m elif force_imdb_id_match: # Only IMDB ID found for some items if tmdb_id: imdb_id = self.tmdb.get_imdb_id(tmdb_id) elif tvdb_id: imdb_id = self.tvdb.get_imdb_id(tvdb_id) if imdb_id and str(imdb_id) in item_ids: imdb_map[imdb_id] = m else: imdb_map[m.ratingKey] = m else: imdb_map[m.ratingKey] = m # Modify the sort titles if self.recipe['new_library']['sort']: print(u"Setting the sort titles for the '{}' library...".format( self.recipe['new_library']['name'])) if self.recipe['new_library']['sort_title']['absolute']: for i, m in enumerate(item_list): item = imdb_map.pop(m['id'], None) if not item: item = imdb_map.pop('tmdb' + str(m.get('tmdb_id', '')), None) if not item: item = imdb_map.pop('tvdb' + str(m.get('tvdb_id', '')), None) if item and self.recipe['new_library']['sort']: self.plex.set_sort_title( new_library.key, item.ratingKey, i + 1, m['title'], self.library_type, self.recipe['new_library']['sort_title']['format'], self.recipe['new_library']['sort_title']['visible']) else: i = 0 for m in item_list: item = imdb_map.pop(m['id'], None) if not item: item = imdb_map.pop('tmdb' + str(m.get('tmdb_id', '')), None) if not item: item = imdb_map.pop('tvdb' + str(m.get('tvdb_id', '')), None) if item and self.recipe['new_library']['sort']: i += 1 self.plex.set_sort_title( new_library.key, item.ratingKey, i, m['title'], self.library_type, self.recipe['new_library']['sort_title']['format'], self.recipe['new_library']['sort_title']['visible']) if self.recipe['new_library']['remove_from_library'] \ or self.recipe['new_library'].get('remove_old', False): # Remove items from the new library which no longer qualify print(u"Removing symlinks for items " "which no longer qualify ".format( library=self.recipe['new_library']['name'])) count = 0 updated_paths = [] deleted_items = [] max_date = add_years( (self.recipe['new_library']['max_age'] or 0) * -1) if self.library_type == 'movie': for movie in imdb_map.values(): if not self.recipe['new_library']['remove_from_library']: # Only remove older than max_age if not self.recipe['new_library']['max_age'] \ or (max_date < movie.originallyAvailableAt): continue for part in movie.iterParts(): old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = os.path.relpath( old_path, self.recipe['new_library']['folder']) if folder_name == '.': new_path = os.path.join( self.recipe['new_library']['folder'], file_name) dir = False else: new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) dir = True if (dir and os.path.exists(new_path)) or ( not dir and os.path.isfile(new_path)): try: if os.name == 'nt': # Python 3.2+ only if sys.version_info < (3, 2): assert os.path.islink(new_path) if dir: os.rmdir(new_path) else: os.remove(new_path) else: assert os.path.islink(new_path) os.unlink(new_path) count += 1 deleted_items.append(movie) updated_paths.append(new_path) except Exception as e: print(u"Remove symlink failed for " "{path}: {e}".format(path=new_path, e=e)) else: for tv_show in imdb_map.values(): done = False if done: continue for episode in tv_show.episodes(): if done: break for part in episode.iterParts(): if done: break old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = '' new_library_folder = \ self.recipe['new_library']['folder'] old_path = os.path.join( new_library_folder, old_path.replace(new_library_folder, '').strip( os.sep).split(os.sep)[0]) folder_name = os.path.relpath( old_path, new_library_folder) new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) if os.path.exists(new_path): try: if os.name == 'nt': # Python 3.2+ only if sys.version_info < (3, 2): assert os.path.islink(new_path) os.rmdir(new_path) else: assert os.path.islink(new_path) os.unlink(new_path) count += 1 deleted_items.append(tv_show) updated_paths.append(new_path) done = True break except Exception as e: print(u"Remove symlink failed for " "{path}: {e}".format(path=new_path, e=e)) else: done = True break print(u"Removed symlinks for {count} items.".format(count=count)) for item in deleted_items: print(u"{title} ({year})".format(title=item.title, year=item.year)) # Scan the library to clean up the deleted items print(u"Scanning the '{library}' library...".format( library=self.recipe['new_library']['name'])) new_library.update() time.sleep(10) new_library = self.plex.server.library.section( self.recipe['new_library']['name']) while new_library.refreshing: time.sleep(5) new_library = self.plex.server.library.section( self.recipe['new_library']['name']) new_library.emptyTrash() all_new_items = new_library.all() else: while imdb_map: imdb_id, item = imdb_map.popitem() i += 1 self.plex.set_sort_title( new_library.key, item.ratingKey, i, item.title, self.library_type, self.recipe['new_library']['sort_title']['format'], self.recipe['new_library']['sort_title']['visible']) return missing_items, len(all_new_items)
def _run(self): force_imdb_id_match = False item_list, item_ids = self._get_source_list_urls() item_list = self._apply_weighted_sorting(item_list) source_libraries = self._get_source_libraries() # Create a list of matching items matching_items = [] missing_items = [] matching_total = 0 nonmatching_idx = [] max_count = self.recipe['new_library']['max_count'] for i, item in enumerate(item_list): match = False if max_count > 0 and matching_total >= max_count: nonmatching_idx.append(i) continue results = self._get_show_results(source_libraries, item) if not results: missing_items.append((i, item)) nonmatching_idx.append(i) continue for result in results: imdb_id, tmdb_id, tvdb_id = self._get_show_ids(result) if self._imdb_matches(imdb_id, item) or \ self._tmdb_matches(tmdb_id, item) or \ self._tvdb_matches(tvdb_id, item): if not match: match = True matching_total += 1 matching_items.append(result) if match: if self.recipe['new_library']['sort_title']['absolute']: print(u"{} {} ({})".format(i + 1, item['title'], item['year'])) else: print(u"{} {} ({})".format(matching_total, item['title'], item['year'])) else: missing_items.append((i, item)) nonmatching_idx.append(i) if not self.recipe['new_library']['sort_title']['absolute']: for i in reversed(nonmatching_idx): del item_list[i] # Create symlinks for all items in your library on the trakt watched print(""" Creating symlinks for {count} matching items in the library...""".format( \ count=matching_total)) try: if not os.path.exists(self.recipe['new_library']['folder']): os.mkdir(self.recipe['new_library']['folder']) except: print(u"Unable to create the new library folder " u"'{folder}'.".format( folder=self.recipe['new_library']['folder'])) print(u"Exiting script.") return 0 count = 0 updated_paths = [] new_items = [] if self.library_type == 'movie': for movie in matching_items: for part in movie.iterParts(): old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = '' for library_config in self.source_library_config: for fldr in library_config['folders']: fldr = os.path.abspath(fldr) if old_path.lower().startswith(fldr.lower()): folder_name = os.path.relpath(old_path, fldr) break else: continue if folder_name == '.': new_path = os.path.join( self.recipe['new_library']['folder'], file_name) drct = False else: new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) drct = True parent_path = os.path.dirname( os.path.abspath(new_path)) if not os.path.exists(parent_path): try: os.makedirs(parent_path) except OSError as err: if err.errno == errno.EEXIST \ and os.path.isdir(parent_path): pass else: raise # Clean up old, empty directories if os.path.exists(new_path) \ and not os.listdir(new_path): os.rmdir(new_path) if (drct and not os.path.exists(new_path)) \ or not drct and not os.path.isfile(new_path): try: if os.name == 'nt': if drct: subprocess.call([ 'mklink', '/D', new_path, old_path ], shell=True) else: subprocess.call([ 'mklink', new_path, old_path_file ], shell=True) else: if drct: os.symlink(old_path, new_path) else: os.symlink(old_path_file, new_path) count += 1 new_items.append(movie) updated_paths.append(new_path) except Exception as err: print(u"Symlink failed for {path}: {error}". format(path=new_path, error=err)) else: for tv_show in matching_items: done = False if done: continue for episode in tv_show.episodes(): if done: break for part in episode.iterParts(): old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = '' for library_config in self.source_library_config: for fldr in library_config['folders']: if old_path.lower().startswith(fldr.lower()): old_path = \ os.path.join(fldr, \ old_path.replace(fldr, \ '').strip(os.sep).split( \ os.sep)[0]) folder_name = os.path.relpath( old_path, fldr) break else: continue new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) if not os.path.exists(new_path): try: if os.name == 'nt': subprocess.call([ 'mklink', '/D', new_path, old_path ], shell=True) else: os.symlink(old_path, new_path) count += 1 new_items.append(tv_show) updated_paths.append(new_path) done = True break except Exception as err: print( u"Symlink failed for {path}: {error}". format(path=new_path, error=err)) else: done = True break print(u"Created symlinks for {count} new items:".format(count=count)) for item in new_items: print(u"{title} ({year})".format(title=item.title, year=item.year)) # Check if the new library exists in Plex print(u"Creating the '{}' library in Plex...".format( self.recipe['new_library']['name'])) try: new_library = self.plex.server.library.section( self.recipe['new_library']['name']) print(u"Library already exists in Plex. Scanning the library...") new_library.update() except plexapi.exceptions.NotFound: self.plex.create_new_library(self.recipe['new_library']['name'], self.recipe['new_library']['folder'], self.library_type) new_library = self.plex.server.library.section( self.recipe['new_library']['name']) # Wait for metadata to finish downloading before continuing print(u"Waiting for metadata to finish downloading...") new_library = self.plex.server.library.section( self.recipe['new_library']['name']) while new_library.refreshing: time.sleep(5) new_library = self.plex.server.library.section( self.recipe['new_library']['name']) # Retrieve a list of items from the new library print(u"Retrieving a list of items from the '{library}' library in " u"Plex...".format(library=self.recipe['new_library']['name'])) all_new_items = new_library.all() # Create a dictionary of {imdb_id: item} imdb_map = {} for mymovie in all_new_items: imdb_id = None tmdb_id = None tvdb_id = None if mymovie.guid is not None and 'imdb://' in mymovie.guid: imdb_id = mymovie.guid.split('imdb://')[1].split('?')[0] elif mymovie.guid is not None and 'themoviedb://' in mymovie.guid: tmdb_id = mymovie.guid.split('themoviedb://')[1].split('?')[0] elif mymovie.guid is not None and 'thetvdb://' in mymovie.guid: tvdb_id = ( \ mymovie.guid.split('thetvdb://')[1].split('?')[0].split('/')[0]) else: imdb_id = None if imdb_id and str(imdb_id) in item_ids: imdb_map[imdb_id] = mymovie elif tmdb_id and ('tmdb' + str(tmdb_id)) in item_ids: imdb_map['tmdb' + str(tmdb_id)] = mymovie elif tvdb_id and ('tvdb' + str(tvdb_id)) in item_ids: imdb_map['tvdb' + str(tvdb_id)] = mymovie elif force_imdb_id_match: # Only IMDB ID found for some items if tmdb_id: imdb_id = self.tmdb.get_imdb_id(tmdb_id) elif tvdb_id: imdb_id = self.tvdb.get_imdb_id(tvdb_id) if imdb_id and str(imdb_id) in item_ids: imdb_map[imdb_id] = mymovie else: imdb_map[mymovie.ratingKey] = mymovie else: imdb_map[mymovie.ratingKey] = mymovie # Modify the sort titles if self.recipe['new_library']['sort']: print(u"Setting the sort titles for the '{}' library...".format( self.recipe['new_library']['name'])) if self.recipe['new_library']['sort_title']['absolute']: for i, mymovie in enumerate(item_list): item = imdb_map.pop(mymovie['id'], None) if not item: item = imdb_map.pop( 'tmdb' + str(mymovie.get('tmdb_id', '')), None) if not item: item = imdb_map.pop( 'tvdb' + str(mymovie.get('tvdb_id', '')), None) if item and self.recipe['new_library']['sort']: self.plex.set_sort_title( new_library.key, item.ratingKey, i + 1, mymovie['title'], self.library_type, self.recipe['new_library']['sort_title']['format'], self.recipe['new_library']['sort_title']['visible']) else: i = 0 for mymovie in item_list: item = imdb_map.pop(mymovie['id'], None) if not item: item = imdb_map.pop( 'tmdb' + str(mymovie.get('tmdb_id', '')), None) if not item: item = imdb_map.pop( 'tvdb' + str(mymovie.get('tvdb_id', '')), None) if item and self.recipe['new_library']['sort']: i += 1 self.plex.set_sort_title( new_library.key, item.ratingKey, i, mymovie['title'], self.library_type, self.recipe['new_library']['sort_title']['format'], self.recipe['new_library']['sort_title']['visible']) if self.recipe['new_library']['remove_from_library'] \ or self.recipe['new_library'].get('remove_old', False): # Remove items from the new library which no longer qualify print(u"Removing symlinks for items " "which no longer qualify ".format( library=self.recipe['new_library']['name'])) count = 0 updated_paths = [] deleted_items = [] max_date = add_years( (self.recipe['new_library']['max_age'] or 0) * -1) if self.library_type == 'movie': for movie in imdb_map.values(): if not self.recipe['new_library']['remove_from_library']: # Only remove older than max_age if not self.recipe['new_library']['max_age'] \ or (max_date < movie.originallyAvailableAt): continue for part in movie.iterParts(): old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = os.path.relpath( old_path, self.recipe['new_library']['folder']) if folder_name == '.': new_path = os.path.join( self.recipe['new_library']['folder'], file_name) drct = False else: new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) drct = True if (drct and os.path.exists(new_path)) or ( not drct and os.path.isfile(new_path)): try: if os.name == 'nt': # Python 3.2+ only if sys.version_info < (3, 2): assert os.path.islink(new_path) if drct: os.rmdir(new_path) else: os.remove(new_path) else: assert os.path.islink(new_path) os.unlink(new_path) count += 1 deleted_items.append(movie) updated_paths.append(new_path) except Exception as err: print(u"Remove symlink failed for " "{path}: {error}".format(path=new_path, error=err)) else: for tv_show in imdb_map.values(): done = False if done: continue for episode in tv_show.episodes(): if done: break for part in episode.iterParts(): if done: break old_path_file = part.file old_path, file_name = os.path.split(old_path_file) folder_name = '' new_library_folder = \ self.recipe['new_library']['folder'] old_path = os.path.join( new_library_folder, old_path.replace(new_library_folder, '').strip( os.sep).split(os.sep)[0]) folder_name = os.path.relpath( old_path, new_library_folder) new_path = os.path.join( self.recipe['new_library']['folder'], folder_name) if os.path.exists(new_path): try: if os.name == 'nt': # Python 3.2+ only if sys.version_info < (3, 2): assert os.path.islink(new_path) os.rmdir(new_path) else: assert os.path.islink(new_path) os.unlink(new_path) count += 1 deleted_items.append(tv_show) updated_paths.append(new_path) done = True break except Exception as err: print(u"Remove symlink failed for " "{path}: {e}".format(path=new_path, err=err)) else: done = True break print(u"Removed symlinks for {count} items.".format(count=count)) for item in deleted_items: print(u"{title} ({year})".format(title=item.title, year=item.year)) # Scan the library to clean up the deleted items print(u"Scanning the '{library}' library...".format( library=self.recipe['new_library']['name'])) new_library.update() time.sleep(10) new_library = self.plex.server.library.section( self.recipe['new_library']['name']) while new_library.refreshing: time.sleep(5) new_library = self.plex.server.library.section( self.recipe['new_library']['name']) new_library.emptyTrash() all_new_items = new_library.all() else: while imdb_map: imdb_id, item = imdb_map.popitem() i += 1 self.plex.set_sort_title( new_library.key, item.ratingKey, i, item.title, self.library_type, self.recipe['new_library']['sort_title']['format'], self.recipe['new_library']['sort_title']['visible']) return missing_items, len(all_new_items)