def __init__(self): self.name = 'TVDbExtension' self.tvdb = tvdb_api.Tvdb(actors=True) self.supported_fflags = [fflags.SHOW_FLAG, fflags.SHOW_DIRECTORY_FLAG] self.supported_season_fflags = [fflags.SEASON_DIRECTORY_FLAG] self.supported_subtitle_fflags = []
def setup_class(cls): if cls.t is None: cls.t = tvdb_api.Tvdb(cache=get_test_cache_session(), banners=False)
def test_episode_name_spanish(self): """Check episode data is in Spanish (language="es") """ t = tvdb_api.Tvdb(cache=get_test_cache_session(), language="es") assert t['scrubs'][1][1]['episodeName'] == u'Mi primer día' assert t['scrubs']['overview'].startswith(u'Scrubs es una divertida comedia')
def setUp(self): if self.t_dvd is None: self.t_dvd = tvdb_api.Tvdb(cache=True, useZip=True, dvdorder=True) if self.t_air is None: self.t_air = tvdb_api.Tvdb(cache=True, useZip=True)
def setUp(self): if self.t is None: self.__class__.t = tvdb_api.Tvdb(cache=True, banners=False)
######################################################## ######################################################## cartoonsLeft = cartoons.copy() showDirectory = [] commercialList = [] commercialSpecific = {} previousRandomShow = 999 playlistDuration = 0 showDurations = [] showName = "" showDesc = "" showCounter = 0 showLength = 0 blockCounter = 0 t = tvdb_api.Tvdb() extensions = ['.264', '.3g2', '.3gp', '.3gp2', '.3gpp', '.3gpp2', '.3mm', '.3p2', '.60d', '.787', '.89', '.aaf', '.aec', '.aep', '.aepx', '.aet', '.aetx', '.ajp', '.ale', '.am', '.amc', '.amv', '.amx', '.anim', '.aqt', '.arcut', '.arf', '.asf', '.asx', '.avb', '.avc', '.avd', '.avi', '.avp', '.avs', '.avs', '.avv', '.axm', '.bdm', '.bdmv', '.bdt2', '.bdt3', '.bik', '.bin', '.bix', '.bmk', '.bnp', '.box', '.bs4', '.bsf', '.bvr', '.byu', '.camproj', '.camrec', '.camv', '.ced', '.cel', '.cine', '.cip', '.clpi', '.cmmp', '.cmmtpl', '.cmproj', '.cmrec', '.cpi', '.cst', '.cvc', '.cx3', '.d2v', '.d3v', '.dat', '.dav', '.dce', '.dck', '.dcr', '.dcr', '.ddat', '.dif', '.dir', '.divx', '.dlx', '.dmb', '.dmsd', '.dmsd3d', '.dmsm', '.dmsm3d', '.dmss', '.dmx', '.dnc', '.dpa', '.dpg', '.dream', '.dsy', '.dv', '.dv-avi', '.dv4', '.dvdmedia', '.dvr', '.dvr-ms', '.dvx', '.dxr', '.dzm', '.dzp', '.dzt', '.edl', '.evo', '.eye', '.ezt', '.f4p', '.f4v', '.fbr', '.fbr', '.fbz', '.fcp', '.fcproject', '.ffd', '.flc', '.flh', '.fli', '.flv', '.flx', '.gfp', '.gl', '.gom', '.grasp', '.gts', '.gvi', '.gvp', '.h264', '.hdmov', '.hkm', '.ifo', '.imovieproj', '.imovieproject', '.ircp', '.irf', '.ism', '.ismc', '.ismv', '.iva', '.ivf', '.ivr', '.ivs', '.izz', '.izzy', '.jss', '.jts', '.jtv', '.k3g', '.kmv', '.ktn', '.lrec', '.lsf', '.lsx', '.m15', '.m1pg', '.m1v', '.m21', '.m21', '.m2a', '.m2p', '.m2t', '.m2ts', '.m2v', '.m4e', '.m4u', '.m4v', '.m75', '.mani', '.meta', '.mgv', '.mj2', '.mjp', '.mjpg', '.mk3d', '.mkv', '.mmv', '.mnv', '.mob', '.mod', '.modd', '.moff', '.moi', '.moov', '.mov', '.movie', '.mp21', '.mp21',
def populate(self, config): import tvdb_api image_cache = config["image_cache"] tvdb = tvdb_api.Tvdb(apikey=THETVDB_APIKEY, banners=True) identifier = int(self.identifier) tv_show = tvdb[identifier] if tv_show.data["seriesName"] == "** 403: Series Not Permitted **": return self.genres.clear() self.network = None self.content_rating = None self.primary_language = None self.status = None self.imdb_id = None self.title = tv_show.data["seriesName"] self.rating = tv_show.data["siteRating"] self.votes = tv_show.data["siteRatingCount"] self.duration = tv_show.data["runtime"] if tv_show.data.get("imdb_id"): imdb_id = re.findall("tt(\d+)", tv_show.data["imdbId"]) if imdb_id: self.imdb_id = imdb_id[0] self.year = None if tv_show.data.get("firstAired"): self.year = tv_show.data.get("firstAired").split("-")[0] self.synopsis = tv_show.data["overview"] if (tv_show.data.get("_banners", {}).get( "poster", {}).get("raw")): # this is retarded design posters = sorted( tv_show.data["_banners"]["poster"]["raw"], key=lambda x: x["ratingsInfo"]["count"], ) if posters: best_id = posters[-1]["id"] for _, v in tv_show.data["_banners"]["poster"].items(): if best_id in v: self.cover = v[best_id]["_bannerpath"] image_cache.get_image_path(self.cover) break if tv_show.data.get("network"): self.network, _ = Network.objects.get_or_create( name=tv_show.data["network"]) if tv_show.data.get("rating"): self.content_rating, _ = ContentRating.objects.get_or_create( name=tv_show.data["rating"]) if tv_show.data.get("language"): self.primary_language, _ = Language.objects.get_or_create( name=tv_show.data["language"]) if tv_show.data.get("status"): self.status, _ = Status.objects.get_or_create( name=tv_show.data["status"]) if tv_show.data["genre"]: for genre in tv_show.data["genre"]: genre, _ = Genre.objects.get_or_create(name=genre) self.genres.add(genre) Episode.objects.filter(metadata=self).delete() for season, episodes in tv_show.items(): for episode, episode_info in episodes.items(): e = Episode(metadata=self, season=season, episode=episode) e.title = episode_info["episodeName"] if episode_info.get("firstAired"): e.air_date = date(*[ int(x) for x in episode_info["firstAired"].split("-") ]) e.save()
#richiedere una password senza visualizzarla from getpass import getpass getpass("messaggio da mostrare") #Elencare i file in una cartella a = os.listdir(os.path.expanduser("~/Media/Film")) b = [] for i in a: b.append(i.split('.')[0]) #Elimino le estensioni ###### #TvDB# ###### #Utilizzo di TvDB per ottenere le info sulle serie tv import tvdb_api as tvdb t = tvdb.Tvdb(language='it') #Setto la lingua italiana a = os.listdir(os.path.expanduser("~/Media/Serie TV")) t['Adventure Time'] for i in a: try: print i + str(t[i][1][5]) except: print "ERROR %s" % i t['bobs burgers'] t['i simpson'].search("La Paura Fa Novanta", key='episodename') t['i simpson'].search("Gli Piace Volare E D'oh Lo Fa", key='episodename') t['i simpson'].search("Gli Piace Volare", key='episodename')
def __init__(self, interactive=True): self.tvdb = tvdb_api.Tvdb(interactive=interactive, banners=True, actors=True, apikey=d(api_key))
def resolve_episode_ids(series, season, episode, year=None, imdbid=None): # To store the IDs found ids = {} # Initialize a TMDB search object tmdb_search = tmdbsimple.Search() ################################################################## # TVDB tvdb = tvdb_api.Tvdb(language='en', ) tvdb_series = None try: # Try getting the series directly, but check the year if available # as sometimes series have the same name but are from different years tvdb_series = tvdb[series] if (imdbid is None or tvdb_series['imdbId'] != imdbid) and \ year is not None and tvdb_series['firstAired'] and \ year != int(tvdb_series['firstAired'].split('-')[0]): # It is not the expected year, we thus need to perform a search tvdb_series = None tvdb_search = tvdb.search(series) for s in tvdb_search: if imdbid is None or s['imdbId'] != imdbid: if not s['seriesName'].startswith(series): LOGGER.debug('TVDB: Discarding result because of the ' 'name not beginning with the expected ' 'series name: {}'.format(s)) continue if int(s['firstAired'].split('-')[0]) != year: LOGGER.debug('TVDB: Discarding result because of the ' 'year not matching: {}'.format(s)) continue tvdb_series = tvdb[s['seriesName']] break tvdb_season = tvdb_series[season] tvdb_episode = tvdb_season[episode] ids['tvdb'] = tvdb_episode['id'] except Exception as e: LOGGER.debug(e) LOGGER.warning('Unable to find series {}, season {}, ' 'episode {} on TheTVDB'.format(series, season, episode)) ################################################################## # TMDB params = {'query': series} if year is not None: params['first_air_date_year'] = year try: tmdb_search.tv(**params) except Exception as e: LOGGER.debug(e) tmdb_search.results = [] for s in tmdb_search.results: if s['name'] != series and \ (tvdb_series is None or (s['name'] != tvdb_series['seriesName'] and s['name'] not in tvdb_series['aliases'])): LOGGER.debug('TMDB: Discarding result because of the name ' 'not matching with the expected series ' 'name: {}'.format(s)) continue # Try to get the episode information tmdb_episode = tmdbsimple.TV_Episodes(s['id'], season, episode) try: tmdb_external_ids = tmdb_episode.external_ids() except Exception as e: continue # If we have the tvdb information, check that we got the right # id... else, it is probably not the episode we are looking # for! if 'tvdb' in ids and \ tmdb_external_ids.get('tvdb_id') is not None and \ ids['tvdb'] != tmdb_external_ids['tvdb_id']: LOGGER.debug('TMDB: Discarding result because of the TVDB id not ' 'matching with the one found on the TVDB ' 'side: {}'.format(s)) continue ids['tmdb'] = tmdb_external_ids['id'] break return ids
def __init__(self): super(TTVDBScanner, self).__init__() self.web_api = tvdb_api.Tvdb(apikey="5BA46DFDB0AB740E", actors=True, banners=True, language='fr')
def main(): parser = argparse.ArgumentParser() # TODO support sources as wildcards parser.add_argument('sources', type=cli.argparse_path, nargs='+', help='paths to source directories/files') parser.add_argument('dst', type=cli.argparse_path, help='path to destination directory') # TODO add argument groups parser.add_argument('-vk', default=False, action='store_true', help='keep source video') parser.add_argument('-vr', default=False, action='store_true', help='recode video') parser.add_argument('-vc', choices=VideoCodec.get_names( [VideoCodec.H264, VideoCodec.H265]), default=VideoCodec.H264.name, help='set video encoding codec') add_enum_argument(parser, '-vt', InputTune, None, 'set video encoding tune') add_enum_argument(parser, '-vq', InputQuality, None, 'set video encoding quality') parser.add_argument('-va', default=None, choices=['16:9'], help='set video display aspect ratio') parser.add_argument('-vs', default=None, choices=['720p', '1080p', '1440p'], help='scale video') parser.add_argument('-ks', default=False, action='store_true', help='keep current colorspace') parser.add_argument('-cr', default=False, action='store_true', help='crop video') parser.add_argument('-cf', type=cli.argparse_path, default=None, metavar='<path>', help='path to crop map file') parser.add_argument('-sc', default=False, action='store_true', help='use same crop values for all files') parser.add_argument( '-al', nargs='*', type=cli.argparse_lang, default=[], metavar='lang', help='ordered list of audio 3-letter language codes to keep') parser.add_argument('-ar', default=False, action='store_true', help='recode audio') parser.add_argument('-ad', default='5.1', choices=CHANNEL_SCHEMES.keys(), help='downmix to N channels') parser.add_argument('-aw', default=False, action='store_true', help='convert audio to wavfile before encoding') parser.add_argument( '-sl', nargs='*', type=cli.argparse_lang, default=[], metavar='lang', help='ordered list of full subtitle 3-letter language codes to keep') parser.add_argument( '-fl', nargs='*', type=cli.argparse_lang, default=[], metavar='lang', help='ordered list of forced subtitle 3-letter language codes to keep' ) parser.add_argument('-fo', default=False, action='store_true', help='make forced subtitles optional') parser.add_argument('-nf', type=cli.argparse_path, default=None, metavar='<path>', help='path to names map file') parser.add_argument('-tv', default=None, metavar='<name>', help='TV series name') parser.add_argument('-il', default=False, action='store_true', help='Ignore track language data') parser.add_argument('-xx', default=False, action='store_true', help='Remove original files after processing') parser.add_argument('-ma', default=False, action='store_true', help='Append mux file instead of overwrite') parser.add_argument('-ds', default=False, action='store_true', help='Disable movie sattelites detection') parser.add_argument('-sd', default=False, action='store_true', help='Securely delete files using sdelete utility') args = parser.parse_args() if args.cf and args.sc: parser.error(u'Use -cf or -sc, not both') if args.nf and args.tv: parser.error(u'Use -nf or -tv, not both') if args.vk and (args.vr or args.vt): parser.error(u'Use -vk or -vr/-vt, not both') if not args.vk and (not args.vt or not args.vq): parser.error(u'Set -vt and -vq') args.vc = VideoCodec.get_definition(args.vc) if args.vt: args.vt = InputTune.get_definition(args.vt) if args.vq: args.vq = InputQuality.get_definition(args.vq) def read_movie_path(path): path = os.path.normpath(path.strip()) if is_media_file_path(path): path = os.path.splitext(path)[0] return path def read_crop_args(s): s = s.strip() if s.lower() == 'no': return False return [int(x) for x in s.split(':')] filenames_map = read_map_file(args.nf, read_movie_path, read_movie_path) raw_crops_map = read_map_file(args.cf, read_movie_path, read_crop_args) tvdb = None if args.tv: tvdb = tvdb_api.Tvdb() movies = {} crop_args_map = None if raw_crops_map is None else {} for argspath in args.sources: for movie_object in find_movies(argspath, args.il, not args.ds): cur_path = os.path.normpath( os.path.relpath(movie_object.main_path(), platform.getcwd())) if raw_crops_map is not None: crop_args_map[movie_object.main_path()] = raw_crops_map[ os.path.splitext(cur_path)[0]] if args.tv: movie_name = os.path.basename(movie_object.main_path()) src_season = int( re.match(r'.*s(\d+)', movie_name, re.IGNORECASE).group(1)) src_episodes = [ int(x) for x in re.findall(r'e(\d+)', movie_name, re.IGNORECASE) ] ep_numbers = [] ep_names = set() for src_episode in src_episodes: ep_info = tvdb[args.tv][src_season][src_episode] epn_dvd = ep_info['dvdEpisodeNumber'] epn_air = ep_info['airedEpisodeNumber'] if epn_dvd is not None and epn_dvd != epn_air: epn_dvd_int = int(float(epn_dvd)) epn_dvd_frc = float(epn_dvd) - epn_dvd_int assert int(epn_air) == int( round(epn_dvd_int + (epn_dvd_frc - 0.1) * 10)), 'd{} a{}'.format( epn_dvd, epn_air) ep_numbers.append(int(epn_air)) ep_names.add( re.sub(r'\(\d\)$', '', ep_info['episodename']).strip()) assert len(ep_numbers) > 0 and len(ep_names) == 1, ep_names cur_path = u'{} {}.mkv'.format( '-'.join(u'{:02d}'.format(epn) for epn in sorted(ep_numbers)), list(ep_names)[0]) elif filenames_map is not None: raw_new_name_string = filenames_map[os.path.splitext(cur_path) [0]] cur_path = None if raw_new_name_string == 'NO': continue elif raw_new_name_string == 'KEEP': cur_path = cur_path else: cur_path = raw_new_name_string if is_media_file_path(cur_path): cur_path = os.path.splitext(cur_path)[0] new_name = u'{}.mkv'.format( platform.clean_filename(os.path.basename(cur_path))) new_path = os.path.join(os.path.abspath(args.dst), os.path.dirname(cur_path), new_name) assert new_path not in movies, new_path movies[new_path] = movie_object output_track_specs = collections.OrderedDict([ ((TrackType.VID, False), ['und']), ((TrackType.AUD, False), args.al), ((TrackType.SUB, False), args.sl), ((TrackType.SUB, True), args.fl), ]) if not (args.ma and os.path.isfile(MUX_BODY)): try: os.remove(MUX_BODY) except: pass shutil.copyfile(MUX_HEAD, MUX_BODY) created_directories = set() # TODO catch some of my exceptions, report skipped file, ask for action, log skipped file common_crop_args = None for target_path, movie in sorted(movies.iteritems(), key=lambda m: m[1].main_path()): platform.print_string(u'=== {} ==='.format(movie.main_path())) output_tracks = {} for (track_type, _) in output_track_specs.iterkeys(): output_tracks[track_type] = [] used_tracks = set() reference_duration = movie.reference_duration() or 0 duration_threshold = reference_duration / 100.0 * 20.0 for (track_type, search_forced), lang_list in output_track_specs.iteritems(): forced_string = 'Forced' if search_forced else 'Full' for target_lang in lang_list: candidates = {} for track in movie.tracks(track_type): if track.qualified_id() in used_tracks: continue if track.language() not in ( target_lang, 'und') and target_lang != 'und': continue if any(s in track.name().lower() for s in [u'comment', u'коммент']): continue if track.is_forced() is not None: if track.is_forced() != search_forced: continue if not track.is_forced() and track.duration( ) is not None: if reference_duration - track.duration( ) > duration_threshold: continue candidates[track.qualified_id()] = track if not candidates: if search_forced and args.fo: continue raise cli.Error(u'{} {} {} not found'.format( forced_string, track_type, target_lang)) chosen_track_id = None if len(candidates) == 1: chosen_track_id = list(candidates.keys())[0] sorted_candidates = sorted(candidates.itervalues(), key=lambda t: t.qualified_id()) if chosen_track_id not in candidates: candidates_by_index = {} for track in sorted_candidates: candidates_by_index[movie.track_index_in_type( track)] = track.qualified_id() header = u'--- {}, {}, {} ---'.format( track_type, target_lang.upper(), forced_string) # TODO if tv AND if tracks ids, names and codecs same as before then choose track automatically chosen_track_index = ask_to_select_tracks( movie, track_type, sorted_candidates, header) chosen_track_id = candidates_by_index[chosen_track_index] used_tracks.add(chosen_track_id) chosen_track = candidates[chosen_track_id] chosen_track.set_language(target_lang) chosen_track.set_forced(search_forced) output_tracks[track_type].append(chosen_track) assert len(output_tracks[TrackType.VID]) == 1 video_track = output_tracks[TrackType.VID][0] def track_sort_key(t): lng_idx = output_track_specs[(t.type(), t.is_forced())].index(t.language()) return lng_idx + 1000 * int(t.is_forced()) track_sources = {} for track_type, track_list in output_tracks.iteritems(): track_list.sort(key=track_sort_key) for track in track_list: track_sources[track.qualified_id()] = [ track.source_file(), track.id() ] result_commands = [u'echo {}'.format(cmd.quote(movie.main_path()))] mux_temporary_files = [] target_directory = os.path.dirname(target_path) if not os.path.isdir(target_directory ) and target_directory not in created_directories: result_commands.extend(cmd.gen_create_dir(target_directory)) created_directories.add(target_directory) def make_single_track_file(track, stream_id, file_ext=None, ffmpeg_opts=None, prefer_ffmpeg=True): if file_ext is None: file_ext = track.get_single_track_file_extension() if ffmpeg_opts is None: ffmpeg_opts = ['-c:{} copy'.format(stream_id)] if file_ext == platform.file_ext( track.source_file()) and track.is_single(): return track.source_file(), False tmp_path = platform.make_temporary_file(file_ext) if not prefer_ffmpeg and platform.file_ext( track.source_file()) == '.mkv': command = cmd.gen_mkvtoolnix_extract_track( track.source_file(), tmp_path, track.id()) else: command = cmd.gen_ffmpeg_extract_track(track.source_file(), tmp_path, track.id(), [], ffmpeg_opts) result_commands.extend(command) return tmp_path, True # TODO move to software abstraction source_container_supported_by_mkvmerge = video_track.container_format( ) not in {FileFormat.x3GP, FileFormat.SMK, FileFormat.WMV} source_video_codec = video_track.codec() source_video_crf = video_track.crf() source_video_profile = video_track.profile() source_video_level = video_track.level() target_video_codec = args.vc target_video_profile, target_video_level = CODEC_FFMPEG_PARAMETERS[ target_video_codec] encoded_ok = source_video_codec == target_video_codec and \ source_video_crf is not None and \ source_video_profile == target_video_profile and \ source_video_level == target_video_level if args.vr or args.vs or not encoded_ok and not args.vk: ffmpeg = Ffmpeg() target_crf, target_tune = CODEC_TUNES[target_video_codec][args.vt][ args.vq] # TODO check out rutracker manuals for dvd rip filters and stuff ffmpeg_filters = [] assert video_track.field_order() is not None if video_track.field_order() in (FieldOrder.INTERLACED_BOT, FieldOrder.INTERLACED_TOP): # TODO consider bwdif ffmpeg_filters.append('yadif=1:-1:1') if args.va: ffmpeg_filters.append('setdar=dar={}'.format(args.va)) crop_args = None if args.cr or args.cf: if common_crop_args is not None: crop_args = common_crop_args if crop_args_map is not None: crop_args = crop_args_map[video_track.source_file()] if crop_args is None: os.system('ffmpegyag') while crop_args is None: try: crop_args = [ int(x) for x in raw_input( 'Enter crop parameters (w:h:x:y): ').strip( ).split(':') ] except: pass if args.sc: common_crop_args = crop_args if crop_args is None or not crop_args: crop_args = [video_track.width(), video_track.height(), 0, 0] dw, dh, dx, dy = crop_args if not VideoTrack.dimensions_correct(dw, dh): platform.print_string(u'Adjusting crop by {}x{}'.format( dw % 16, dh % 8)) dw, dh, dx, dy = VideoTrack.correct_dimensions(dw, dh, dx, dy) assert VideoTrack.dimensions_correct(dw, dh) if dx > 0 or dy > 0 or dw != video_track.width( ) or dh != video_track.height(): ffmpeg_filters.append('crop={w}:{h}:{x}:{y}'.format(w=dw, h=dh, x=dx, y=dy)) # TODO support different resolutions # TODO forbid upscale if args.vs == '720p': ffmpeg_filters.append('scale=1280:-8') elif args.vs == '1080p': ffmpeg_filters.append('scale=1920:-8') elif args.vs == '1440p': # TODO !!!!!!!!!! ffmpeg_filters.append('scale=-16:1440') src_colors = video_track.colors() dst_color_space = src_colors.correct_space() if args.ks: dst_color_space = src_colors.space() if src_colors.space() != dst_color_space: raise cli.Error( u'Colorspace conversion from {} to {} not implemented'. format(src_colors.space(), dst_color_space)) # TODO specify input/output color_range # TODO specify each input component separately # TODO The input transfer characteristics, color space, color primaries and color range should be set on the input data # TODO clarify iall=all= format string # ffmpeg_filters.append('colorspace=iall={}:all={}'.format(src_color_space, dst_color_space)) ffmpeg_src_options = [] src_colors_range = src_colors.range() if src_colors_range is not None: ffmpeg_src_options.append('-color_range {}'.format( ffmpeg.build_color_range_argument(src_colors_range))) ffmpeg_dst_options = ['-an', '-sn', '-dn'] if ffmpeg_filters: ffmpeg_dst_options.append('-filter:v {}'.format( ','.join(ffmpeg_filters))) ffmpeg_dst_options.extend([ '-c:v {}'.format( ffmpeg.build_video_encoding_library_argument( target_video_codec)), '-preset veryslow', '-pix_fmt {}'.format( ffmpeg.build_picture_format_argument( PictureFormat.YUV420P)), '-crf {}'.format(target_crf), '-map_metadata -1', '-map_chapters -1', ]) arg_profile = ffmpeg.build_video_codec_profile_argument( target_video_codec, target_video_profile) arg_level = ffmpeg.build_video_codec_level_argument( target_video_codec, target_video_level) if target_video_codec == VideoCodec.H264: ffmpeg_dst_options.extend([ '-profile:v {}'.format(arg_profile), '-level:v {}'.format(arg_level) ]) elif target_video_codec == VideoCodec.H265: ffmpeg_dst_options.append( '-x265-params "profile={}:level={}"'.format( arg_profile, arg_level)) if target_tune is not None: ffmpeg_dst_options.append('-tune {}'.format(target_tune)) if dst_color_space is not None and ( video_track.is_hd() or src_colors.space() is not None): ffmpeg_dst_options.extend([ # TODO "16-235 is a typical NTSC luma range. PAL always uses 0-255 luma range." '-color_range {}'.format( ffmpeg.build_color_range_argument(src_colors.range())), '-color_primaries {}'.format( ffmpeg.build_color_primaries_argument( dst_color_space)), '-color_trc {}'.format( ffmpeg.build_color_trc_argument(dst_color_space)), '-colorspace {}'.format( ffmpeg.build_color_space_argument(dst_color_space)), ]) else: assert not video_track.is_hd() new_video_path = platform.make_temporary_file('.mkv') result_commands.extend( cmd.gen_ffmpeg_convert(video_track.source_file(), ffmpeg_src_options, new_video_path, ffmpeg_dst_options)) track_sources[video_track.qualified_id()] = [new_video_path, 0] mux_temporary_files.append(new_video_path) elif not source_container_supported_by_mkvmerge: new_video_path, _ = make_single_track_file( video_track, Ffmpeg.STREAM_ARGUMENT_VID, '.mkv') track_sources[video_track.qualified_id()] = [new_video_path, 0] mux_temporary_files.append(new_video_path) # TODO move to software abstraction audio_codecs_to_denorm = {AudioCodec.AC3, AudioCodec.DTS} audio_codecs_to_uncompress = { AudioCodec.AAC_HE, AudioCodec.AAC_HE_V2, AudioCodec.AAC_LC, AudioCodec.AMR, AudioCodec.OPUS, AudioCodec.SPEEX, AudioCodec.COOK, AudioCodec.ASAO, AudioCodec.ADPCM_SWF, AudioCodec.PCM_MULAW, AudioCodec.PCM_S16B, AudioCodec.VORBIS, AudioCodec.SMK, AudioCodec.WMA_PRO, AudioCodec.WMA_V2, } audio_codecs_to_recode = { AudioCodec.AMR, AudioCodec.ASAO, AudioCodec.OPUS, AudioCodec.SPEEX, AudioCodec.COOK, AudioCodec.EAC3, AudioCodec.DTS_ES, AudioCodec.DTS_HRA, AudioCodec.DTS_MA, AudioCodec.TRUE_HD, AudioCodec.ADPCM_IMA, AudioCodec.ADPCM_MS, AudioCodec.ADPCM_SWF, AudioCodec.PCM_MULAW, AudioCodec.PCM_S16B, AudioCodec.PCM_S16L, AudioCodec.PCM_S24L, AudioCodec.FLAC, AudioCodec.MP2, AudioCodec.VORBIS, AudioCodec.SMK, AudioCodec.WMA_PRO, AudioCodec.WMA_V2 } max_audio_channels = CHANNEL_SCHEMES[args.ad] for track in output_tracks[TrackType.AUD]: need_extract = not source_container_supported_by_mkvmerge need_denorm = track.codec() in audio_codecs_to_denorm need_downmix = track.channels() > max_audio_channels need_recode = need_downmix or track.codec( ) in audio_codecs_to_recode or args.ar and track.codec( ) != AudioCodec.AAC_LC need_uncompress = track.codec( ) in audio_codecs_to_uncompress or args.aw if need_extract or need_denorm or need_downmix or need_recode: stf_ext = None stf_ffmpeg_opts = None if need_uncompress: stf_ext = '.wav' stf_ffmpeg_opts = ['-f wav', '-rf64 auto'] src_track_file, is_src_track_file_temporary = make_single_track_file( track, Ffmpeg.STREAM_ARGUMENT_AUD, stf_ext, stf_ffmpeg_opts) eac_track_file = src_track_file if need_denorm or need_downmix or need_recode: eac_track_file = platform.make_temporary_file( '.wav' if need_recode else platform. file_ext(src_track_file)) eac_opts = [] if need_downmix: if max_audio_channels == 1: pass # will be processed later elif max_audio_channels == 2: eac_opts.append('-downStereo') elif max_audio_channels == 6: eac_opts.append('-down6') else: raise cli.Error( u'Unhandled channels num {}'.format( max_audio_channels)) if track.delay() != 0: eac_opts.append('{}{}ms'.format( '+' if track.delay() > 0 else '-', abs(track.delay()))) result_commands.append(u'call eac3to {} {} {}'.format( cmd.quote(src_track_file), cmd.quote(eac_track_file), ' '.join(eac_opts))) if is_src_track_file_temporary: result_commands.extend( cmd.gen_del_files(args.sd, src_track_file)) dst_track_file = eac_track_file if need_downmix and max_audio_channels == 1: mono_track_file = platform.make_temporary_file('.wav') result_commands.extend( cmd.gen_ffmpeg_convert(eac_track_file, [], mono_track_file, ['-ac 1'])) result_commands.extend( cmd.gen_del_files(args.sd, eac_track_file)) dst_track_file = mono_track_file if need_recode: m4a_track_file = platform.make_temporary_file('.m4a') qaac_opts = [ '--tvbr 91', '--quality 2', '--rate keep', '--no-delay' ] qaac = u'qaac64 {} {} -o {}'.format( u' '.join(qaac_opts), cmd.quote(dst_track_file), cmd.quote(m4a_track_file)) result_commands.append(qaac) result_commands.extend( cmd.gen_del_files(args.sd, dst_track_file)) dst_track_file = m4a_track_file mux_temporary_files.append(dst_track_file) track_sources[track.qualified_id()] = [dst_track_file, 0] for track in output_tracks[TrackType.SUB]: if track.is_text(): ffmpeg_opts = None if track.codec() == SubtitleCodec.MOV: ffmpeg_opts = [] track_file, is_track_file_temporary = make_single_track_file( track, Ffmpeg.STREAM_ARGUMENT_SUB, ffmpeg_opts=ffmpeg_opts) srt_file = platform.make_temporary_file('.srt') result_commands.append( u'{python} {script} {src_path} {dst_path}'.format( python=sys.executable, script=cmd.quote( os.path.join(os.path.dirname(__file__), 'any2srt.py')), src_path=cmd.quote(track_file), dst_path=cmd.quote(srt_file))) track_sources[track.qualified_id()] = [srt_file, 0] track.set_encoding(lang.norm_encoding('utf-8')) mux_temporary_files.append(srt_file) if is_track_file_temporary: result_commands.extend( cmd.gen_del_files(args.sd, track_file)) elif track.codec() == SubtitleCodec.PGS: track_file, is_track_file_temporary = make_single_track_file( track, Ffmpeg.STREAM_ARGUMENT_SUB, prefer_ffmpeg=False) idx_file = platform.make_temporary_file('.idx') sub_file = u'{}.sub'.format(os.path.splitext(idx_file)[0]) result_commands.extend( cmd.gen_bdsup2sub(track_file, idx_file, lang.alpha2(track.language()))) track_sources[track.qualified_id()] = [idx_file, 0] mux_temporary_files.extend([idx_file, sub_file]) if is_track_file_temporary: result_commands.extend( cmd.gen_del_files(args.sd, track_file)) mux_path = platform.make_temporary_file('.mkv') # TODO add cover to files mux = ['call', 'mkvmerge'] mux.extend(['--output', cmd.quote(mux_path)]) mux.extend([ '--no-track-tags', '--no-global-tags', '--disable-track-statistics-tags' ]) track_ids_by_files = {} for qualified_id, (source_file, source_file_track_id) in track_sources.iteritems(): track_ids_by_files.setdefault( source_file, {})[qualified_id] = source_file_track_id if source_container_supported_by_mkvmerge: track_ids_by_files.setdefault(video_track.source_file(), {}) # TODO tracks need to be extracted from 3gp and wmv containers before passing to mkvmerge source_file_ids = {} for i, (source_file, track_ids_map) in enumerate(track_ids_by_files.iteritems()): source_file_ids[source_file] = i for track_type, (tracks_flags_yes, tracks_flag_no) in Track.TYPE_FLAGS.iteritems(): cur_file_tracks = [ track for track in output_tracks[track_type] if track.qualified_id() in track_ids_map ] if cur_file_tracks: if tracks_flags_yes: mux.append('{} {}'.format( tracks_flags_yes, ','.join( str(track_ids_map[track.qualified_id()]) for track in cur_file_tracks))) for track in cur_file_tracks: default = track.qualified_id( ) == output_tracks[track_type][0].qualified_id() file_track_id = track_ids_map[track.qualified_id()] mux.append('--track-name {0}:""'.format(file_track_id)) if track_type == TrackType.SUB and track.encoding( ) is not None: mux.append('--sub-charset {0}:{1}'.format( file_track_id, track.encoding())) mux.append('--language {0}:{1}'.format( file_track_id, track.language())) mux.append('--default-track {0}:{1}'.format( file_track_id, 'yes' if default else 'no')) if track.is_forced(): mux.append( '--forced-track {0}:yes'.format(file_track_id)) elif tracks_flag_no: mux.append(tracks_flag_no) file_flags = [ '--no-track-tags', '--no-attachments', '--no-buttons', '--no-global-tags' ] if source_file != video_track.source_file(): file_flags.append('--no-chapters') mux.append(u'{} {}'.format(u' '.join(file_flags), cmd.quote(source_file))) mux.append('--title ""') track_order = [] for track_type in [TrackType.VID, TrackType.AUD, TrackType.SUB]: for track in output_tracks[track_type]: source_file, source_file_track_id = track_sources[ track.qualified_id()] track_order.append('{}:{}'.format(source_file_ids[source_file], source_file_track_id)) mux.append('--track-order {}'.format(','.join(track_order))) result_commands.append(u' '.join(mux)) if len(mux_temporary_files) > 0: result_commands.extend( cmd.gen_del_files(args.sd, *sorted(set(mux_temporary_files)))) # TODO mark mkv file with mkvexport version if movie.chapters_path() is not None: result_commands.append(u'mkvpropedit --chapters {} {}'.format( cmd.quote(movie.chapters_path()), cmd.quote(mux_path))) clean_mux_path = platform.make_temporary_file('.mkv') result_commands.append(u'call mkclean {} {}'.format( cmd.quote(mux_path), cmd.quote(clean_mux_path))) result_commands.extend(cmd.gen_del_files(args.sd, mux_path)) result_commands.extend( cmd.gen_move_file(clean_mux_path, target_path, args.sd)) if args.xx: result_commands.extend( cmd.gen_del_files( args.sd, *sorted( set(media_file.path() for media_file in movie.media_files())))) allowed_exit_codes = {'robocopy': 1, 'mkvmerge': 1} with codecs.open(MUX_BODY, 'a', 'utf-8') as body_file: for command in result_commands: fail_exit_code = 1 for program, code in allowed_exit_codes.iteritems(): if program in command.lower(): fail_exit_code = code + 1 stop_statement = u'call :stop {}'.format( misc.random_printable(8)) if fail_exit_code == 1: prepared_commands = [ u'{} || {}'.format(command.strip(), stop_statement) ] else: prepared_commands = [ command.strip(), u'if errorlevel {} {}'.format(fail_exit_code, stop_statement) ] for prep_command in prepared_commands: body_file.write(u'{}\r\n'.format(prep_command)) body_file.write(u'\r\n') return 0
def fset(self, value): self._api_params['language'] = value self._api = tvdb.Tvdb(**self._api_params) self._language = value
def __init__(self, media, tvdbid=None, lang=None, confidence=0.0, query_thresh=1.0, interactive=False, grabber=None, formatter=formatters.formatter_default, interface=FileSystemInterface): """media : str or unicode Path to file, directory or glob pattern to media files. interactive : bool When true, tvdb_api interactive console is used to select from multiple results. grabber : tvdb_ui.BaseUI sublcass instance or None Grabs appropriate TVDB query result. If None, uses default grabber. Else, this option overrides `interactive`. If None, uses default QueryGrabber. formatter : scrappy.formatters.Formatter instance Object defining file name format to output. The default `Formatter` outputs file names in `seriesname.SXXEXX.episodename.ext` format. tvdbid : int or str TVDB ID number for the show being queried. lang : str or None Two-character language abbreviation (e.g.: 'en') confidence : float or int Minimum confidence index to consider a series-name inference as valid. query_thresh : float or int Maximum error to accept a result returned by TheTVDB. Default 1.0: accept all TVDB results. NOTE: ignored if not using default grabber. interface : AbstractMediaInterface class object Object inhereting from AbstractMediaInterface base class. The interface object provides a layer of abstraction for working with local files or abstract files (strings representing file names). By default, the FileSystemInterface class object is selected. """ self.normalized_seriesname = None # TVDB api if not grabber and not interactive: grabber = partial(QueryGrabber, parent=self, thresh=query_thresh) self._api_params = { 'language': lang, 'search_all_languages': lang == None, 'apikey': self._api_key, 'interactive': interactive, 'custom_ui': grabber } self._api = tvdb.Tvdb( **self._api_params ) # TODO: render interactive and implement a custom UI # Other params self.id = tvdbid self.language = lang # input validated in tvdb.Tvdb self.series = None if tvdbid: # tolerate users who pass str if isinstance(tvdbid, str) or isinstance(tvdbid, unicode): tvdbid = int(tvdbid.strip()) # Files self._files = interface(media) self.filemap = dict((fname, None) for fname in self._files) self.revert_filenames = self._files.revert self.formatter = formatter if not self.id: self._guess_series_name(confidence)
def setup_class(cls): if cls.t is None: cls.t = tvdb_api.Tvdb(cache=True, banners=False)
def __init__(self, datadir, configfile, daemon=False): # Verify if the log directory exists or create it logdir = os.path.join(datadir, 'logs') if not os.path.exists(logdir): os.mkdir(logdir) # Process log file name if daemon: if LOG_LEVEL is logging.DEBUG: logfile = os.path.join(logdir, "TraktForVLC-DEBUG.log") # Remove existing DEBUG file if os.path.isfile(logfile): os.remove(logfile) else: logfile = os.path.join( logdir, "TraktForVLC-" + DATETIME.strftime("%Y%m%d-%H%M") + ".log") logging.basicConfig( format="%(asctime)s::%(name)s::%(levelname)s::%(message)s", level=LOG_LEVEL, filename=logfile) else: logging.basicConfig( format="%(asctime)s::%(name)s::%(levelname)s::%(message)s", level=LOG_LEVEL, stream=sys.stderr) self.log = logging.getLogger("TraktForVLC") e = 'e' if sys.platform.lower().startswith('win') else 'ë' self.log.info( "## TraktForVLC v" + __version__ + " " + __release_name__) self.log.info("## Copyright (C) 2014-2015 " + "Rapha" + e + "l Beamonte <*****@*****.**>") self.log.info("##") self.log.info("## TraktForVLC is distributed in the hope that it " + "will be useful, but") self.log.info("## with ABSOLUTELY NO WARRANTY. This is free " + "software; you are welcome") self.log.info("## to redistribute and/or modify it under the terms " + "of the GPL2.") self.log.info("") if not os.path.isfile(configfile): self.log.error("Config file " + configfile + " not found, exiting.") sys.exit(1) self.log.debug("Running on %s, with Python %s" % ( platform.platform(), platform.python_version())) self.__check_version() # Load configuration self.configfile = configfile self.__load_config() for loglvl, logstr in AVAILABLE_LOGLVL: if LOG_LEVEL <= loglvl: loglevelstr = logstr break if loglevelstr is None: loglevelstr = str(LOG_LEVEL) self.log.info("Logger level is set to %s" % loglevelstr) self.log.info("-- Will scrobble movies ? %s" % ( 'Yes' if self.DO_SCROBBLE_MOVIE else 'No')) self.log.info("-- Will scrobble tv shows ? %s" % ( 'Yes' if self.DO_SCROBBLE_TV else 'No')) self.log.info("-- Will we mark movies as being watched ? %s" % ( 'Yes' if self.DO_WATCHING_MOVIE else 'No')) self.log.info("-- Will we mark tv shows as being watched ? %s" % ( 'Yes' if self.DO_WATCHING_TV else 'No')) self.log.info("-- Videos will be scrobbled after " + str(self.SCROBBLE_PERCENT) + "% of their duration has been exceeded") self.log.info("-- Timer set to " + str(self.TIMER_INTERVAL) + " secs") self.log.info("-- Video will be marked as \"is watching\" from " + str(self.START_WATCHING_TIMER) + " secs") # VLC configuration self.vlc_ip = self.config.get("VLC", "IP") self.vlc_port = self.config.getint("VLC", "Port") self.log.info("Listening VLC to " + self.vlc_ip + ":" + str(self.vlc_port)) # Trakt app configuration trakt_id = ("0e59f99095515c228d5fbc104e342574" + "941aeeeda95946b8fa50b2b0366609bf") trakt_sc = ("3ed1d013ef80eb0bb45d8da8424b4b61" + "3713abb057ed505683caf0baf1b5c650") # Trakt user information trakt = { "PIN": None, "access_token": None, "refresh_token": None, "Username": None, # Now deprecated in Trakt v2 API "Password": None, # Now deprecated in Trakt v2 API } for opt in trakt.keys(): if self.config.has_option("Trakt", opt): trakt[opt] = self.config.get("Trakt", opt) # Initialize Trakt client modifiedTime = time.strftime( '%Y-%m-%d', time.gmtime(os.path.getmtime(get_file()))) self.trakt_client = TraktClient.TraktClient({ 'username': trakt['Username'], 'password': trakt['Password'], 'client_id': trakt_id, 'client_secret': trakt_sc, 'app_version': __version__, 'app_date': modifiedTime, 'pin': trakt['PIN'], 'access_token': trakt['access_token'], 'refresh_token': trakt['refresh_token'], 'callback_token': self.__callback_token_change, }) # Initialize TraktForVLC's cache self.resetCache() # Initialize tvdb api self.tvdb = tvdb_api.Tvdb(cache=False, language='en') self.watching_now = "" self.vlcTime = 0 self.vlc_connected = True
def __init__(self, datadir, configfile): # Verify if the log directory exists or create it logdir = os.path.join(datadir, 'logs') if not os.path.exists(logdir): os.mkdir(logdir) # Process log file name if LOG_LEVEL is logging.DEBUG: logfile = os.path.join(logdir, "TraktForVLC-DEBUG.log") # Remove existing DEBUG file if os.path.isfile(logfile): os.remove(logfile) else: logfile = os.path.join( logdir, "TraktForVLC-" + DATETIME.strftime("%Y%m%d-%H%M") + ".log") logging.basicConfig( format="%(asctime)s::%(name)s::%(levelname)s::%(message)s", level=LOG_LEVEL, filename=logfile, stream=sys.stdout) self.log = logging.getLogger("TraktForVLC") self.log.info("## TraktForVLC v" + __version__ + " " + __release_name__) self.log.info("## Copyright (C) 2014-2015 " + "Raphaël Beamonte <*****@*****.**>") self.log.info("##") self.log.info("## TraktForVLC is distributed in the hope that it " + "will be useful, but") self.log.info("## with ABSOLUTELY NO WARRANTY. This is free " + "software; you are welcome") self.log.info("## to redistribute and/or modify it under the terms " + "of the GPL2.") self.log.info("") if not os.path.isfile(configfile): self.log.error("Config file " + configfile + " not found, exiting.") exit() self.__check_version() self.config = ConfigParser.RawConfigParser() self.config.read(configfile) # Initialize timers if SMALL_TIMERS: self.TIMER_INTERVAL = 5 self.START_WATCHING_TIMER = 5 else: self.TIMER_INTERVAL = int(self.config.get("TraktForVLC", "Timer")) self.START_WATCHING_TIMER = int( self.config.get("TraktForVLC", "StartWatching")) # For the use of filenames instead of VLC window title self.USE_FILENAME = (True if self.config.get( "TraktForVLC", "UseFilenames") == 'Yes' else False) # Do we have to scrobble ? self.DO_SCROBBLE_MOVIE = (True if self.config.get( "TraktForVLC", "ScrobbleMovie") == 'Yes' else False) self.DO_SCROBBLE_TV = (True if self.config.get( "TraktForVLC", "ScrobbleTV") == 'Yes' else False) # Do we have to mark as watching ? self.DO_WATCHING_MOVIE = (True if self.config.get( "TraktForVLC", "WatchingMovie") == 'Yes' else False) self.DO_WATCHING_TV = (True if self.config.get( "TraktForVLC", "WatchingTV") == 'Yes' else False) # What percent should we use to scrobble videos ? self.SCROBBLE_PERCENT = int( self.config.get("TraktForVLC", "ScrobblePercent")) for loglvl, logstr in AVAILABLE_LOGLVL: if LOG_LEVEL <= loglvl: loglevelstr = logstr break if loglevelstr is None: loglevelstr = str(LOG_LEVEL) self.log.info("Logger level is set to %s" % loglevelstr) self.log.info("-- Will scrobble movies ? %s" % ('Yes' if self.DO_SCROBBLE_MOVIE else 'No')) self.log.info("-- Will scrobble tv shows ? %s" % ('Yes' if self.DO_SCROBBLE_TV else 'No')) self.log.info("-- Will we mark movies as being watched ? %s" % ('Yes' if self.DO_WATCHING_MOVIE else 'No')) self.log.info("-- Will we mark tv shows as being watched ? %s" % ('Yes' if self.DO_WATCHING_TV else 'No')) self.log.info("-- Videos will be scrobbled after " + str(self.SCROBBLE_PERCENT) + "% of their duration has been exceeded") self.log.info("-- Timer set to " + str(self.TIMER_INTERVAL) + " secs") self.log.info("-- Video will be marked as \"is watching\" from " + str(self.START_WATCHING_TIMER) + " secs") # VLC configuration self.vlc_ip = self.config.get("VLC", "IP") self.vlc_port = self.config.getint("VLC", "Port") self.log.info("Listening VLC to " + self.vlc_ip + ":" + str(self.vlc_port)) # Trakt configuration trakt_api = ("0e59f99095515c228d5fbc104e342574" + "941aeeeda95946b8fa50b2b0366609bf") trakt_username = self.config.get("Trakt", "Username") trakt_password = self.config.get("Trakt", "Password") self.log.info("Connect to Trakt(" + trakt_username + ", *********)") # Initialize Trakt client modifiedTime = time.strftime('%Y-%m-%d', time.gmtime(os.path.getmtime(__file__))) self.trakt_client = TraktClient.TraktClient(trakt_username, trakt_password, trakt_api, __version__, modifiedTime) self.resetCache() # Initialize tvdb api self.tvdb = tvdb_api.Tvdb(cache=False, language='en') self.watching_now = "" self.vlcTime = 0 self.vlc_connected = True
def command_ep(bot, user, channel, args): """Usage: ep <series name>""" if not api_ok: return t = tvdb_api.Tvdb() now = datetime.now() # one day resolution maximum now = now.replace(hour=0, minute=0, second=0, microsecond=0) # prevent "Series '' not found" if not args: return try: series = t[args] except tvdb_exceptions.tvdb_shownotfound: bot.say(channel, "Series '%s' not found" % args) return future_episodes = [] all_episodes = [] # find all episodes with airdate > now for season_no, season in series.items(): for episode_no, episode in season.items(): firstaired = episode['firstaired'] if not firstaired: continue airdate = datetime.strptime(firstaired, "%Y-%m-%d") td = airdate - now all_episodes.append(episode) # list all unaired episodes if td >= timedelta(0, 0, 0): future_episodes.append(episode) # if any future episodes were found, find out the one with airdate closest to now if future_episodes: # sort the list just in case it's out of order (specials are season 0) future_episodes = sorted(future_episodes, key=itemgetter('firstaired')) episode = future_episodes[0] td = datetime.strptime(episode['firstaired'], "%Y-%m-%d") - now if td.days == 1: airdate = "tomorrow" elif td.days > 1: airdate = "%s (%d days)" % (episode['firstaired'], td.days) else: airdate = "today" season_ep = "%dx%02d" % (int(float(episode['combined_season'])), int(float(episode['combined_episodenumber']))) msg = "Next episode of %s %s '%s' airs %s" % ( series.data['seriesname'], season_ep, episode['episodename'], airdate) # no future episodes found, show the latest one elif all_episodes: # find latest episode of the show all_episodes = sorted(all_episodes, key=itemgetter('firstaired')) episode = all_episodes[-1] ## episode age in years and days td = now - datetime.strptime(episode['firstaired'], "%Y-%m-%d") years, days = td.days // 365, td.days % 365 agestr = [] if years >= 1: agestr.append("%d years" % years) if days > 0: agestr.append("%d days" % days) airdate = "%s (%s ago)" % (episode['firstaired'], " ".join(agestr)) season_no = int(episode['combined_season']) # the episode number is sometimes returned as a float, hack it. episode_no = int(float(episode['combined_episodenumber'])) season_ep = "%dx%02d" % (season_no, episode_no) msg = "Latest episode of %s %s '%s' aired %s" % ( series.data['seriesname'], season_ep, episode['episodename'], airdate) else: msg = "No new or past episode airdates found for %s" % series.data[ 'seriesname'] bot.say(channel, msg.encode("UTF-8"))
from django.db import models from django.core.urlresolvers import reverse import threading import time import tvdb_api _t = tvdb_api.Tvdb(cache=False) def _active_sleep(interval): # Sleep, but occasionally wake to check the system time. # Takes into account time when the system is off. sleep_time = min(60, interval / 10) start = time.time() while time.time() - start < interval: time.sleep(sleep_time) class ShowManager(models.Manager): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._update_thread_started = False def from_tvdb(self, id_, populate=True): # Load a show from the tvdb using a TVDB id. try: # get show if it already exists show = Show.objects.get(pk=id_) # populate show if necessary if show.populated or not populate:
def __init__(self): # todo: selectfirst=False self.tvdb = tvdb_api.Tvdb(banners=True, actors=True)
path = sys.argv[1] for fn in os.listdir(path): if os.path.isfile(path + fn): # Split out file extension bn, ext = os.path.splitext(fn) if bn in matches: nbn = matches[bn] else: nbn = search_episode_by_filename(bn) matches[bn] = nbn print("{} -> {}".format(bn, nbn)) os.rename(path + fn, path + nbn + ext) if __name__ == "__main__": t = tvdb_api.Tvdb(language='de') show = t[TVDB_TATORT_SHOW_ID] # Build a dict of all show titles, indexed by the TVDB episode ID # Note: we need to use dicts here and not lists, as fuzzywuzzy only returns # the matching ID when using a dict, not a list. tatort_episodes = {} tatort_titles = {} for cur_season in show.values(): for cur_episode in cur_season.values(): episode_id = cur_episode['id'] # Prepare the title string used for matching the filename # We filter the title as we get it from TVDB to contain only the # title. Example: # "Episode 2016x39 - Janneke & Brix - 05 - Land in dieser Zeit"
def tvdb_fetch(series, season, episode): t = tvdb_api.Tvdb() episode = t[series][season][episode] return episode
def setUp(self): if self.t is None: self.__class__.t = tvdb_api.Tvdb(cache=True, actors=True)
def tvnamer(paths): # type: (List[str]) -> None """Main tvnamer function, takes an array of paths, does stuff. """ print("#" * 20) print("# Starting tvnamer") episodes_found = [] for cfile in find_files(paths): parser = FileParser(cfile) try: episode = parser.parse() except InvalidFilename as e: warn("Invalid filename: %s" % e) else: if (episode.seriesname is None and Config["force_name"] is None and Config["series_id"] is None): warn( "Parsed filename did not contain series name (and --name or --series-id not specified), skipping: %s" % cfile) else: episodes_found.append(episode) if len(episodes_found) == 0: raise NoValidFilesFoundError() print("# Found %d episode" % len(episodes_found) + ("s" * (len(episodes_found) > 1))) # Sort episodes by series name, season and episode number episodes_found.sort(key=lambda x: x.sortable_info()) # episode sort order if Config["order"] == "dvd": dvdorder = True else: dvdorder = False if Config["tvdb_api_key"] is not None: LOG.debug("Using custom API key from config") api_key = Config["tvdb_api_key"] else: LOG.debug("Using tvnamer default API key") api_key = TVNAMER_API_KEY if os.getenv("TVNAMER_TEST_MODE", "0") == "1": from .test_cache import get_test_cache_session cache = get_test_cache_session() else: cache = True tvdb_instance = tvdb_api.Tvdb( interactive=not Config["select_first"], search_all_languages=Config["search_all_languages"], language=Config["language"], dvdorder=dvdorder, cache=cache, apikey=api_key, ) for episode in episodes_found: process_file(tvdb_instance, episode) print("") print("#" * 20) print("# Done")
"""Test Tvdb.search method """ results = self.t.search("my name is earl") all_ids = [x['id'] for x in results] assert 75397 in all_ids class TestTvdbAltNames: t = None @classmethod def setup_class(cls): if cls.t is None: cls.t = tvdb_api.Tvdb(cache=get_test_cache_session(), actors=True) def test_1(self): """Tests basic access of series name alias """ results = self.t.search("Don't Trust the B---- in Apartment 23") series = results[0] assert 'Apartment 23' in series['aliases'] if __name__ == '__main__': cache = get_test_cache_session() t = tvdb_api.Tvdb(cache=cache) t['scrubs'][1][2] t = tvdb_api.Tvdb(cache=cache) t['scrubs'][1][2] # pytest.main()
def setup_class(cls): if cls.t_dvd is None: cls.t_dvd = tvdb_api.Tvdb(cache=True, dvdorder=True) if cls.t_air is None: cls.t_air = tvdb_api.Tvdb(cache=True)
def test_episode_name_french(self): """Check episode data is in French (language="fr") """ t = tvdb_api.Tvdb(cache=get_test_cache_session(), language="fr") assert t['scrubs'][1][1]['episodeName'] == "Mon premier jour" assert t['scrubs']['overview'].startswith(u"J.D. est un jeune m\xe9decin qui d\xe9bute")
def setup_class(cls): if cls.t is None: cls.t = tvdb_api.Tvdb(cache=True, actors=True)
def setup_class(cls): if cls.t is None: cls.t = tvdb_api.Tvdb(cache=get_test_cache_session(), actors=True)
class SmartUI(tvdb_api.BaseUI): """Returns the latest series that is actually airing""" def selectSeries(self, allSeries): t = tvdb_api.Tvdb() # reverse order, latest shows first(?) for series in reversed(allSeries): # search with ID directly to skip name->ID lookup in library status = t[series['id']].data['status'] if status == "Continuing": return series if len(allSeries) > 0: return allSeries[0] if __name__ == "__main__": api = tvdb_api.Tvdb(custom_ui=SmartUI) print(api['doctor who']) # Doctor Who 2005 print(api['castle']) # Castle 2009 print(api['house of cards']) # House of Cards (US) def command_ep(bot, user, channel, args): """Usage: ep <series name>""" if not api_ok: return t = tvdb_api.Tvdb(custom_ui=SmartUI) now = datetime.now() # one day resolution maximum now = now.replace(hour=0, minute=0, second=0, microsecond=0) # prevent "Series '' not found"