def perform(self): requested = {} for obj in self.metadata: if not obj.isinstance(Episode) and not obj.isinstance(Movie): log.warning('Unknown type for downloading subtitle: %s' % obj.__class__.__name__) continue files = tolist(obj.get('files', [])) if not files: log.warning( "Cannot download subtitle for %s because it doesn't have an attached file" % obj.niceString()) continue # find an appropriate filename videoFilename = files[0].filename filetmpl = '%s.%s' % (os.path.splitext(videoFilename)[0], self.language.english_name) + '%s.srt' subFilename = findAvailableFilename(filetmpl) requested[videoFilename] = subFilename if not requested: raise SmewtException( 'Invalid list of objects for which to download subtitles') # download subtitles self.downloadSubtitles(requested) # validate the subtitles for videoFilename, subFilename in requested.items(): # make sure the files actually exist on disk if not os.path.exists(subFilename): log.warning('Could not download subtitle for file %s' % videoFilename) continue # normalize subtitle file end-of-lines if sys.platform != 'win32': with open(subFilename) as subfile: subtext = subfile.read().replace('\r\n', '\n') with open(subFilename, 'w') as subfile: subfile.write(subtext) # update the database with the found subs db = list(self.metadata)[0].graph() for obj in self.metadata: filenames = [f.filename for f in tolist(obj.get('files', []))] if videoFilename in filenames: # FIXME: the following 2 lines should happen in a transaction sub = db.Subtitle(metadata=obj, language=self.language.alpha2) subfile = db.Media(filename=subFilename, metadata=sub) break else: log.error( 'Internal error: downloaded subfile for non-requested metadata' )
def allSeries(store, database, parent_id=-1, only_available=True): seriesItems = lambda db: tolist(db.find_all('Episode')) if only_available: seriesItems = lambda db: list(itertools.ifilter(is_available, tolist(db.find_all('Episode')))) seriesViews = [ { 'sortItems': lambda items: sorted(items, key = lambda i: i.series.title), 'groupItems': lambda sortedItems: itertools.groupby(sortedItems, key=lambda i: i.series), 'name': lambda k: k.title if 'title' in k else '[unknown]', 'image': lambda k: k.hiresImage if 'hiresImage' in k else None }, { 'sortItems': lambda items: sorted(items, key = lambda i: i.season), 'groupItems': lambda sortedItems: itertools.groupby(sortedItems, key=lambda i: i.season), 'name': lambda k: 'Season %d' % (k,) }, { 'sortItems': lambda items: sorted(items, key = lambda i: int(i.episodeNumber)), 'name': lambda k: '%3d - %s' % (k.get('episodeNumber', 0), k.get('title', tolist(k.get('files', []))[0].get('filename', '[unknown]')), ) } ] items = seriesItems(database) return recursiveContainer(store, items, seriesViews, parent_id=parent_id)
def get_episodes_and_subs(language, series, season=None): if season: episodes = set(ep for ep in tolist(series.episodes) if ep.season == int(season)) else: episodes = set(tolist(series.episodes)) subs = [] for ep in episodes: subs.extend(tolist(ep.get("subtitles"))) return episodes, subs
def get_episodes_and_subs(language, series, season=None): if season: episodes = set(ep for ep in tolist(series.episodes) if ep.season == int(season)) else: episodes = set(tolist(series.episodes)) subs = [] for ep in episodes: subs.extend(tolist(ep.get('subtitles'))) return episodes, subs
def perform(self): requested = {} for obj in self.metadata: if not obj.isinstance(Episode) and not obj.isinstance(Movie): log.warning('Unknown type for downloading subtitle: %s' % obj.__class__.__name__) continue files = tolist(obj.get('files', [])) if not files: log.warning("Cannot download subtitle for %s because it doesn't have an attached file" % obj.niceString()) continue # find an appropriate filename videoFilename = files[0].filename filetmpl = '%s.%s' % (os.path.splitext(videoFilename)[0], self.language.english_name) + '%s.srt' subFilename = findAvailableFilename(filetmpl) requested[videoFilename] = subFilename if not requested: raise SmewtException('Invalid list of objects for which to download subtitles') # download subtitles self.downloadSubtitles(requested) # validate the subtitles for videoFilename, subFilename in requested.items(): # make sure the files actually exist on disk if not os.path.exists(subFilename): log.warning('Could not download subtitle for file %s' % videoFilename) continue # normalize subtitle file end-of-lines if sys.platform != 'win32': with open(subFilename) as subfile: subtext = subfile.read().replace('\r\n', '\n') with open(subFilename, 'w') as subfile: subfile.write(subtext) # update the database with the found subs db = list(self.metadata)[0].graph() for obj in self.metadata: filenames = [ f.filename for f in tolist(obj.get('files', [])) ] if videoFilename in filenames: # FIXME: the following 2 lines should happen in a transaction sub = db.Subtitle(metadata = obj, language = self.language.alpha2) subfile = db.Media(filename = subFilename, metadata = sub) break else: log.error('Internal error: downloaded subfile for non-requested metadata')
def recursiveContainer(store, items, view_funcs, prefix='', parent_id=-1): nameMethod = lambda x: x sortItems = lambda x: x if len(view_funcs) > 0: nameMethod = view_funcs[0].get('name', lambda x: '[unknown]') imageMethod = view_funcs[0].get('image', lambda x: None) sortItems = view_funcs[0].get('sortItems', lambda x: x) itemClass = view_funcs[0].get('item', None) items = sortItems(items) if len(view_funcs) == 0 or 'groupItems' not in view_funcs[0]: children = [] unknown = None for i in items: if itemClass is None: files = tolist(i.files) if len(files)>0 and 'title' in i and i.title == 'Unknown': # These unguessed files are matched to a movie named 'Unknown' unknown = Container(store, '[unknown]', parent_id, image = imageMethod(i)) #print '%s%s [%d]' % (prefix, unicode('Unknown'), unknown.id, ) for f in tolist(i.files): newitem = VideoFileItem(store, f, unicode(os.path.basename(f.filename)), parent_id, image = imageMethod(i)) #print ' %s%s [%d]' % (prefix, unicode(os.path.basename(f.filename)), newitem.id, ) unknown.add_child(newitem) else: # These are the normal files newitem = VideoItem(store, i, unicode(nameMethod(i)), parent_id, image = imageMethod(i)) #print '%s%s [%d]' % (prefix, unicode(nameMethod(i)), newitem.id, ) children.append(newitem) else: newitem = itemClass(store, i, unicode(nameMethod(i)), parent_id, image = imageMethod(i)) #print '%s%s [%d]' % (prefix, unicode(nameMethod(i)), newitem.id, ) children.append(newitem) if unknown is not None: children.append(unknown) return children groupItems = view_funcs[0]['groupItems'] group = groupItems(items) children = [] for k, v in group: cont = Container(store, unicode(nameMethod(k)), parent_id, image = imageMethod(k)) #print '%s%s [%d]' % (prefix, unicode(nameMethod(k)), cont.id, ) cont.add_children( recursiveContainer(store, list(v), view_funcs[1:], parent_id = cont.id, prefix = ' ' + prefix) ) children.append(cont) return children
def allMovies(store, database, parent_id=-1, only_available=True): moviesItems = lambda db: tolist(db.find_all('Movie')) if only_available: moviesItems = lambda db: list(itertools.ifilter(is_available, tolist(db.find_all('Movie')))) moviesViews = [ { 'sortItems': lambda items: sorted(items, key = lambda i: i.title), 'name': lambda k: k.title if 'title' in k else '[unknown]', 'image': lambda k: k.hiresImage if 'hiresImage' in k else None } ] items = moviesItems(database) return recursiveContainer(store, items, moviesViews, parent_id=parent_id)
def perform(self, query): self.checkValid(query) media = query.find_one(node_type=Media) movieMetadata = guess_movie_info(media.filename) movieMetadata = guessitToPygoo(movieMetadata) # FIXME: this is a temporary hack waiting for the pygoo and ontology refactoring if len(tolist(movieMetadata.get('language', None))) > 1: movieMetadata['language'] = movieMetadata['language'][0] averageConfidence = sum( movieMetadata.confidence(prop) for prop in movieMetadata) / len(movieMetadata) # put the result of guessit in a form that smewt understands movie = query.Movie(confidence=averageConfidence, **movieMetadata) msg = u'Found filename information from %s:' % media.filename msg += str(movie).decode('utf-8') log.debug(msg) result = foundMetadata(query, movie) #result.display_graph() return result
def startEpisode(self, episode): self.tmdb.lang = guiLanguage().alpha2 if episode.get('series') is None: raise SmewtException("TVDBMetadataProvider: Episode doesn't contain 'series' field: %s", episode) name = episode.series.title name = name.replace(',', ' ') matching_series = self.getSeries(name) # Try first with the languages from guessit, and then with english languages = tolist(episode.get('language', [])) + ['en'] # Sort the series by id (stupid heuristic about most popular series # might have been added sooner to the db and the db id # follows the insertion order) # TODO: we should do something smarter like comparing series name distance, # episodes count and/or episodes names #print '\n'.join(['%s %s --> %f [%s] %s' % (x[1], name, textutils.levenshtein(x[1], name), x[2], x[0]) for x in matching_series]) matching_series.sort(key=lambda x: (textutils.levenshtein(x[1], name), int(x[0]))) series = None language = 'en' for lang in languages: try: language = lang ind = zip(*matching_series)[2].index(lang) series = matching_series[ind][0] break except ValueError, e: language = matching_series[0][2] series = matching_series[0][0]
def perform(self, query): self.checkValid(query) media = query.find_one(node_type = Media) movieMetadata = guess_movie_info(media.filename) movieMetadata = guessitToPygoo(movieMetadata) # FIXME: this is a temporary hack waiting for the pygoo and ontology refactoring if len(tolist(movieMetadata.get('language', None))) > 1: movieMetadata['language'] = movieMetadata['language'][0] averageConfidence = sum(movieMetadata.confidence(prop) for prop in movieMetadata) / len(movieMetadata) # put the result of guessit in a form that smewt understands movie = query.Movie(confidence = averageConfidence, **movieMetadata) msg = u'Found filename information from %s:' % media.filename msg += str(movie).decode('utf-8') log.debug(msg) result = foundMetadata(query, movie) #result.display_graph() return result
def findOrCreate(self): for c in utils.tolist(self.graph.config.get('collections')): if c.name == self.name: return c result = CollectionSettings.fromDict({ 'name': self.name, 'folders': [] }, self.graph) self.addToConfig(result) return result
def advSeries(store, database, parent_id=-1, only_available=True): def isEpisode(m): from smewt.media.series.serieobject import Episode return os.path.isfile(m.filename) and any([any([mdlink.isinstance(Episode) for mdlink in mds]) for mds in tolist(m.metadata)]) seriesItems = lambda db: list(db.find_all(node_type = 'Media', valid_node = isEpisode)) seriesViews = [ { 'sortItems': lambda items: sorted(items, key = lambda i: tolist(i.metadata)[0].series.title), 'groupItems': lambda sortedItems: itertools.groupby(sortedItems, key=lambda i: tolist(i.metadata)[0].series), 'name': lambda k: k.title if 'title' in k else '[unknown]', 'image': lambda k: k.hiresImage if 'hiresImage' in k else None }, { 'sortItems': lambda items: sorted(items, key = lambda i: tolist(i.metadata)[0].season), 'groupItems': lambda sortedItems: itertools.groupby(sortedItems, key=lambda i: tolist(i.metadata)[0].season), 'name': lambda k: 'Season %d' % (k,) }, { 'sortItems': lambda items: sorted(items, key = lambda i: (int(tolist(i.metadata)[0].episodeNumber), i.filename)), 'name': lambda k: '%3d - %s' % (tolist(k.metadata)[0].get('episodeNumber', 0), tolist(k.metadata)[0].get('title', os.path.basename(k.get('filename', '[unknown]'))), ), 'item': VideoFileItem } ] items = seriesItems(database) return recursiveContainer(store, items, seriesViews, parent_id=parent_id)
def moviesByProperty(store, database, prop, parent_id=-1, only_available=True, getProperty = None, default='Other'): moviesItems = lambda db: tolist(db.find_all('Movie')) if only_available: moviesItems = lambda db: list(itertools.ifilter(is_available, tolist(db.find_all('Movie')))) moviesViews = [ { 'groupItems': lambda sortedItems: groupByProperty(sortedItems, prop, getProperty=getProperty, default=default), 'name': lambda k: k }, { 'sortItems': lambda items: sorted(items, key = lambda i: i.title), 'name': lambda k: k.title if 'title' in k else '[unknown]' } ] items = moviesItems(database) return recursiveContainer(store, items, moviesViews, parent_id=parent_id)
def play_video(metadata, sublang=None): # FIXME: this should be handled properly with media player plugins # files should be a list of (Metadata, sub), where sub is possibly None # then we would look into the available graphs where such a Metadata has files, # and choose the one on the fastest media (ie: local before nfs before tcp) # it should also choose subtitles the same way, so we could even imagine reading # the video from one location and the subs from another # find list of all files to be played # returns a list of (video_filename, sub_filename) if sublang: msg = 'Playing %s with %s subtitles' % (metadata, Language(sublang).english_name) else: msg = 'Playing %s with no subtitles' % metadata log.info(u(msg)) # FIXME: we assume that sorting alphanumerically is good enough, but that is # not necessarily the case... # we should also look whether the file also has the 'cdNumber' attribute files = tolist(metadata.get('files')) files = sorted(files, key=lambda f: f.get('filename')) if sublang is not None: sublang = Language(sublang) for sub in tolist(metadata.get('subtitles')): if sub.language == sublang: subs = sorted(tolist(sub.get('files')), key=lambda f: f.get('filename')) break else: subs = [None] * len(files) # update last viewed info metadata.lastViewed = time.time() metadata.watched = True _play([f.filename for f in files], [s.filename for s in subs if s])
def play_video(metadata, sublang=None): # FIXME: this should be handled properly with media player plugins # files should be a list of (Metadata, sub), where sub is possibly None # then we would look into the available graphs where such a Metadata has files, # and choose the one on the fastest media (ie: local before nfs before tcp) # it should also choose subtitles the same way, so we could even imagine reading # the video from one location and the subs from another # find list of all files to be played # returns a list of (video_filename, sub_filename) if sublang: msg = "Playing %s with %s subtitles" % (metadata, Language(sublang).english_name) else: msg = "Playing %s with no subtitles" % metadata log.info(u(msg)) # FIXME: we assume that sorting alphanumerically is good enough, but that is # not necessarily the case... # we should also look whether the file also has the 'cdNumber' attribute files = tolist(metadata.get("files")) files = sorted(files, key=lambda f: f.get("filename")) if sublang is not None: sublang = Language(sublang) for sub in tolist(metadata.get("subtitles")): if sub.language == sublang: subs = sorted(tolist(sub.get("files")), key=lambda f: f.get("filename")) break else: subs = [None] * len(files) # update last viewed info metadata.lastViewed = time.time() metadata.watched = True _play([f.filename for f in files], [s.filename for s in subs if s])
def playUrl(self): files = utils.tolist(self.get('files')) # prepare link for playing movie without subtitles nfile = 1 args = {} for f in sorted(files, key=lambda f: f.get('filename')): args['filename%d' % nfile] = f.filename nfile += 1 if not args: args['filename1'] = '' # FIXME: doesn't make sense... return SmewtUrl('action', 'play', args)
def get_subtitles(media_type, title, season=None, language=None): db = smewt.SMEWTD_INSTANCE.database language = language or db.config.get('subtitleLanguage') or 'en' if media_type == 'episode': series = db.find_one('Series', title=title) episodes, subs = get_episodes_and_subs(language, series, season) already_good = set(s.metadata for s in subs) episodes = episodes - already_good if episodes: subtask = SubtitleTask(episodes, language) smewt.SMEWTD_INSTANCE.taskManager.add(subtask) return 'OK' else: msg = 'All episodes already have %s subtitles!' % Language( language).english_name log.info(msg) return msg elif media_type == 'movie': movie = db.find_one('Movie', title=title) # check if we already have it for sub in tolist(movie.get('subtitles')): if sub.language == language: msg = 'Movie already has a %s subtitle' % Language( language).english_name log.info(msg) return msg subtask = SubtitleTask(movie, language) smewt.SMEWTD_INSTANCE.taskManager.add(subtask) return 'OK' else: msg = 'Don\'t know how to fetch subtitles for type: %s' % media_type log.error(msg) return msg
def get_subtitles(media_type, title, season=None, language=None): db = smewt.SMEWTD_INSTANCE.database language = language or db.config.get("subtitleLanguage") or "en" if media_type == "episode": series = db.find_one("Series", title=title) episodes, subs = get_episodes_and_subs(language, series, season) already_good = set(s.metadata for s in subs) episodes = episodes - already_good if episodes: subtask = SubtitleTask(episodes, language) smewt.SMEWTD_INSTANCE.taskManager.add(subtask) return "OK" else: msg = "All episodes already have %s subtitles!" % Language(language).english_name log.info(msg) return msg elif media_type == "movie": movie = db.find_one("Movie", title=title) # check if we already have it for sub in tolist(movie.get("subtitles")): if sub.language == language: msg = "Movie already has a %s subtitle" % Language(language).english_name log.info(msg) return msg subtask = SubtitleTask(movie, language) smewt.SMEWTD_INSTANCE.taskManager.add(subtask) return "OK" else: msg = "Don't know how to fetch subtitles for type: %s" % media_type log.error(msg) return msg
def startEpisode(self, episode): self.tmdb.lang = guiLanguage().alpha2 if episode.get('series') is None: raise SmewtException( "TVDBMetadataProvider: Episode doesn't contain 'series' field: %s", episode) name = episode.series.title name = name.replace(',', ' ') matching_series = self.getSeries(name) # Try first with the languages from guessit, and then with english languages = tolist(episode.get('language', [])) + ['en'] # Sort the series by id (stupid heuristic about most popular series # might have been added sooner to the db and the db id # follows the insertion order) # TODO: we should do something smarter like comparing series name distance, # episodes count and/or episodes names #print '\n'.join(['%s %s --> %f [%s] %s' % (x[1], name, textutils.levenshtein(x[1], name), x[2], x[0]) for x in matching_series]) matching_series.sort( key=lambda x: (textutils.levenshtein(x[1], name), int(x[0]))) series = None language = 'en' for lang in languages: try: language = lang ind = zip(*matching_series)[2].index(lang) series = matching_series[ind][0] break except ValueError, e: language = matching_series[0][2] series = matching_series[0][0]
def addToConfig(self, c): if c not in utils.tolist(self.graph.config.get('collections')): self.graph.config.append('collections', c)
def is_available(x): return any([os.path.isfile(f.filename) for f in tolist(x.get('files', []))])
def create_item(self): item = DIDLLite.VideoItem(self.id, self.parent_id, self.get_name()) external_url = '%s/%d@%d' % (self.store.urlbase, self.id, self.parent_id,) # add http resource for videoFile in tolist(self.media.files): filename = videoFile.filename internal_url = 'file://' + filename mimetype, _ = mimetypes.guess_type(filename, strict=False) size = None if os.path.isfile(filename): size = os.path.getsize(filename) res = DIDLLite.Resource(external_url, 'http-get:*:%s:*' % (mimetype,)) res.size = size item.res.append(res) res = DIDLLite.Resource(internal_url, 'internal:%s:%s:*' % (self.store.server.coherence.hostname, mimetype,)) res.size = size item.res.append(res) # FIXME: Handle correctly multifile videos self.location = filename if self.image and os.path.isfile(self.image): mimetype,_ = mimetypes.guess_type(self.image, strict=False) if mimetype in ('image/jpeg','image/png'): if mimetype == 'image/jpeg': dlna_pn = 'DLNA.ORG_PN=JPEG_TN' else: dlna_pn = 'DLNA.ORG_PN=PNG_TN' dlna_tags = simple_dlna_tags[:] dlna_tags[3] = 'DLNA.ORG_FLAGS=00f00000000000000000000000000000' hash_from_path = str(id(self.image)) _, ext = os.path.splitext(self.image) item.albumArtURI = ''.join((external_url,'?cover',ext)) new_res = DIDLLite.Resource(external_url+'?attachment='+hash_from_path, 'http-get:*:%s:%s' % (mimetype, ';'.join([dlna_pn]+dlna_tags))) new_res.size = os.path.getsize(self.image) item.res.append(new_res) if not hasattr(item, 'attachments'): item.attachments = {} item.attachments[hash_from_path] = coherence_utils.StaticFile(self.image) for subtitle in tolist(self.media.get('subtitles')): for subfile in tolist(subtitle.files): subfilename = subfile.filename if os.path.isfile(subfilename): # check for a subtitles file hash_from_path = str(id(subfilename)) mimetype = 'smi/caption' new_res = DIDLLite.Resource(external_url+'?attachment='+hash_from_path, 'http-get:*:%s:%s' % (mimetype, '*')) new_res.size = os.path.getsize(subfilename) self.caption = new_res.data item.res.append(new_res) if not hasattr(item, 'attachments'): item.attachments = {} item.attachments[hash_from_path] = coherence_utils.StaticFile(subfilename) return item
def dispatch(self, mainWidget, surl): if surl.actionType == 'play': # FIXME: this should be handled properly with media player plugins # play action should be a list of (Metadata, sub), where sub is possibly None # then we would look into the available graphs where such a Metadata has files, # and choose the one on the fastest media (ie: local before nfs before tcp) # it should also choose subtitles the same way, so we could even imagine reading # the video from one location and the subs from another # find list of all files to be played args = [] nfile = 1 while 'filename%d' % nfile in surl.args: filename = surl.args['filename%d' % nfile] args.append(filename) # update last viewed info try: media = mainWidget.smewtd.database.find_one(Media, filename = filename) media.metadata.lastViewed = time.time() except: pass nfile += 1 if sys.platform == 'linux2': action = 'xdg-open' # FIXME: xdg-open only accepts 1 argument, this will break movies split in multiple files... args = args[:1] # if we have smplayer installed, use it with subtitles support if os.system('which smplayer') == 0: action = 'smplayer' args = [ '-fullscreen', '-close-at-end' ] nfile = 1 while 'filename%d' % nfile in surl.args: filename = surl.args['filename%d' % nfile] args.append(filename) if 'subtitle%d' % nfile in surl.args: args += [ '-sub', surl.args['subtitle%d' % nfile] ] nfile += 1 elif sys.platform == 'darwin': action = 'open' elif sys.platform == 'win32': action = 'open' log.debug('launching %s with args = %s' % (action, str(args))) mainWidget.externalProcess.startDetached(action, args) elif surl.actionType == 'getsubtitles': if surl.args['type'] == 'episode': title = surl.args['title'] language = surl.args['language'] db = mainWidget.smewtd.database series = db.find_one('Series', title = title) if 'season' in surl.args: seriesEpisodes = set(ep for ep in tolist(series.episodes) if ep.season == int(surl.args['season'])) else: seriesEpisodes = set(tolist(series.episodes)) currentSubs = db.find_all(node_type = 'Subtitle', # FIXME: we shouldn't have to go to the node, but if we don't, the valid_node lambda doesn't return anything... valid_node = lambda x: toresult(list(x.metadata)) in set(ep.node for ep in seriesEpisodes), language = language) alreadyGood = set(s.metadata for s in currentSubs) episodes = seriesEpisodes - alreadyGood if episodes: subtask = SubtitleTask(episodes, language) mainWidget.smewtd.taskManager.add(subtask) else: log.info('All videos already have %s subtitles!' % Language(language).english_name) elif surl.args['type'] == 'movie': title = surl.args['title'] language = surl.args['language'] db = mainWidget.smewtd.database movie = db.find_one('Movie', title = title) # check if we already have it for sub in tolist(movie.get('subtitles')): if sub.language == language: log.info('Movie already has a %s subtitle' % Language(language).english_name) return subtask = SubtitleTask(movie, language) mainWidget.smewtd.taskManager.add(subtask) else: log.error('Don\'t know how to fetch subtitles for type: %s' % surl.args['type']) else: raise SmewtException('Unknown action type: %s' % surl.actionType)
def isEpisode(m): from smewt.media.series.serieobject import Episode return os.path.isfile(m.filename) and any([any([mdlink.isinstance(Episode) for mdlink in mds]) for mds in tolist(m.metadata)])
def loadFeeds(self): db = self._smewtd.database fs = db.config.get('feeds', []) self.feedList = [ f.toDict() for f in tolist(db.config.get('feeds')) ]