Merge remote-tracking branch 'origin/development' into halali

# Conflicts:
#	bazarr/get_subtitle.py
pull/489/head
Halali 6 years ago
commit 2a6dc54c88

@ -3,6 +3,7 @@ import os
import sqlite3
import requests
import logging
import re
from queueconfig import q4ws
from get_args import args
@ -75,7 +76,19 @@ def sync_episodes():
sceneName = episode['episodeFile']['sceneName']
else:
sceneName = None
try:
format, resolution = episode['episodeFile']['quality']['quality']['name'].split('-')
except:
format = episode['episodeFile']['quality']['quality']['name']
resolution = str(episode['episodeFile']['quality']['quality']['resolution']) + 'p'
videoCodec = episode['episodeFile']['mediaInfo']['videoCodec']
videoCodec = SonarrFormatVideoCodec(videoCodec)
audioCodec = episode['episodeFile']['mediaInfo']['audioCodec']
audioCodec = SonarrFormatAudioCodec(audioCodec)
# Add episodes in sonarr to current episode list
current_episodes_sonarr.append(episode['id'])
@ -83,12 +96,14 @@ def sync_episodes():
episodes_to_update.append((episode['title'], episode['episodeFile']['path'],
episode['seasonNumber'], episode['episodeNumber'],
sceneName, str(bool(episode['monitored'])),
episode['id']))
format, resolution,
videoCodec, audioCodec, episode['id']))
else:
episodes_to_add.append((episode['seriesId'], episode['id'], episode['title'],
episode['episodeFile']['path'], episode['seasonNumber'],
episode['episodeNumber'], sceneName,
str(bool(episode['monitored']))))
str(bool(episode['monitored'])), format, resolution,
videoCodec, audioCodec))
removed_episodes = list(set(current_episodes_db_list) - set(current_episodes_sonarr))
@ -97,12 +112,12 @@ def sync_episodes():
c = db.cursor()
updated_result = c.executemany(
'''UPDATE table_episodes SET title = ?, path = ?, season = ?, episode = ?, scene_name = ?, monitored = ? WHERE sonarrEpisodeId = ?''',
'''UPDATE table_episodes SET title = ?, path = ?, season = ?, episode = ?, scene_name = ?, monitored = ?, format = ?, resolution = ?, video_codec = ?, audio_codec = ? WHERE sonarrEpisodeId = ?''',
episodes_to_update)
db.commit()
added_result = c.executemany(
'''INSERT OR IGNORE INTO table_episodes(sonarrSeriesId, sonarrEpisodeId, title, path, season, episode, scene_name, monitored) VALUES (?, ?, ?, ?, ?, ?, ?, ?)''',
'''INSERT OR IGNORE INTO table_episodes(sonarrSeriesId, sonarrEpisodeId, title, path, season, episode, scene_name, monitored, format, resolution, video_codec, audio_codec) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
episodes_to_add)
db.commit()
@ -127,3 +142,28 @@ def sync_episodes():
logging.debug('BAZARR All missing subtitles updated in database.')
q4ws.append('Episodes sync from Sonarr ended.')
def SonarrFormatAudioCodec(audioCodec):
if audioCodec == 'AC-3': return 'AC3'
if audioCodec == 'E-AC-3': return 'EAC3'
if audioCodec == 'MPEG Audio': return 'MP3'
return audioCodec
def SonarrFormatVideoCodec(videoCodec):
if videoCodec == 'x264' or videoCodec == 'AVC': return 'h264'
if videoCodec == 'x265' or videoCodec == 'HEVC': return 'h265'
if videoCodec.startswith('XviD'): return 'XviD'
if videoCodec.startswith('DivX'): return 'DivX'
if videoCodec == 'MPEG-1 Video': return 'Mpeg'
if videoCodec == 'MPEG-2 Video': return 'Mpeg2'
if videoCodec == 'MPEG-4 Video': return 'Mpeg4'
if videoCodec == 'VC-1': return 'VC1'
if videoCodec.endswith('VP6'): return 'VP6'
if videoCodec.endswith('VP7'): return 'VP7'
if videoCodec.endswith('VP8'): return 'VP8'
if videoCodec.endswith('VP9'): return 'VP9'
return videoCodec

@ -73,7 +73,31 @@ def update_movies():
sceneName = movie['movieFile']['sceneName']
else:
sceneName = None
if movie['alternativeTitles'] != None:
alternativeTitles = str([item['title'] for item in movie['alternativeTitles']])
if 'imdbId' in movie: imdbId = movie['imdbId']
else: imdbId = None
try:
format, resolution = movie['movieFile']['quality']['quality']['name'].split('-')
except:
format = movie['movieFile']['quality']['quality']['name']
resolution = movie['movieFile']['quality']['quality']['resolution'].lstrip('r').lower()
videoFormat = movie['movieFile']['mediaInfo']['videoFormat']
videoCodecID = movie['movieFile']['mediaInfo']['videoCodecID']
videoProfile = movie['movieFile']['mediaInfo']['videoProfile']
videoCodecLibrary = movie['movieFile']['mediaInfo']['videoCodecLibrary']
videoCodec = RadarrFormatVideoCodec(videoFormat, videoCodecID, videoProfile, videoCodecLibrary)
audioFormat = movie['movieFile']['mediaInfo']['audioFormat']
audioCodecID = movie['movieFile']['mediaInfo']['audioCodecID']
audioProfile = movie['movieFile']['mediaInfo']['audioProfile']
audioAdditionalFeatures = movie['movieFile']['mediaInfo']['audioAdditionalFeatures']
audioCodec = RadarrFormatAudioCodec(audioFormat, audioCodecID, audioProfile, audioAdditionalFeatures)
# Add movies in radarr to current movies list
current_movies_radarr.append(unicode(movie['tmdbId']))
@ -89,22 +113,27 @@ def update_movies():
movie["tmdbId"], movie["id"], overview, poster, fanart,
profile_id_to_language(movie['qualityProfileId']), sceneName,
unicode(bool(movie['monitored'])), movie['sortTitle'],
movie["tmdbId"]))
movie['year'], alternativeTitles, format, resolution,
videoCodec, audioCodec, imdbId, movie["tmdbId"]))
else:
if movie_default_enabled is True:
movies_to_add.append((movie["title"],
movie["path"] + separator + movie['movieFile'][
'relativePath'], movie["tmdbId"], movie_default_language,
'[]', movie_default_hi, movie["id"], overview, poster, fanart,
movie["path"] + separator + movie['movieFile']['relativePath'],
movie["tmdbId"], movie_default_language, '[]', movie_default_hi,
movie["id"], overview, poster, fanart,
profile_id_to_language(movie['qualityProfileId']), sceneName,
unicode(bool(movie['monitored'])), movie['sortTitle']))
unicode(bool(movie['monitored'])), movie['sortTitle'],
movie['year'], alternativeTitles, format, resolution,
videoCodec, audioCodec, imdbId))
else:
movies_to_add.append((movie["title"],
movie["path"] + separator + movie['movieFile'][
'relativePath'], movie["tmdbId"], movie["tmdbId"],
movie["tmdbId"], movie["id"], overview, poster, fanart,
profile_id_to_language(movie['qualityProfileId']), sceneName,
unicode(bool(movie['monitored'])), movie['sortTitle']))
unicode(bool(movie['monitored'])), movie['sortTitle'],
movie['year'], alternativeTitles, format, resolution,
videoCodec, audioCodec, imdbId))
else:
logging.error(
'BAZARR Radarr returned a movie without a file path: ' + movie["path"] + separator +
@ -115,18 +144,18 @@ def update_movies():
c = db.cursor()
updated_result = c.executemany(
'''UPDATE table_movies SET title = ?, path = ?, tmdbId = ?, radarrId = ?, overview = ?, poster = ?, fanart = ?, `audio_language` = ?, sceneName = ?, monitored = ?, sortTitle= ? WHERE tmdbid = ?''',
'''UPDATE table_movies SET title = ?, path = ?, tmdbId = ?, radarrId = ?, overview = ?, poster = ?, fanart = ?, `audio_language` = ?, sceneName = ?, monitored = ?, sortTitle = ?, year = ?, alternativeTitles = ?, format = ?, resolution = ?, video_codec = ?, audio_codec = ?, imdbId = ? WHERE tmdbid = ?''',
movies_to_update)
db.commit()
if movie_default_enabled is True:
added_result = c.executemany(
'''INSERT OR IGNORE INTO table_movies(title, path, tmdbId, languages, subtitles,`hearing_impaired`, radarrId, overview, poster, fanart, `audio_language`, sceneName, monitored, sortTitle) VALUES (?,?,?,?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
'''INSERT OR IGNORE INTO table_movies(title, path, tmdbId, languages, subtitles,`hearing_impaired`, radarrId, overview, poster, fanart, `audio_language`, sceneName, monitored, sortTitle, year, alternativeTitles, format, resolution, video_codec, audio_codec, imdbId) VALUES (?,?,?,?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
movies_to_add)
db.commit()
else:
added_result = c.executemany(
'''INSERT OR IGNORE INTO table_movies(title, path, tmdbId, languages, subtitles,`hearing_impaired`, radarrId, overview, poster, fanart, `audio_language`, sceneName, monitored, sortTitle) VALUES (?,?,?,(SELECT languages FROM table_movies WHERE tmdbId = ?), '[]',(SELECT `hearing_impaired` FROM table_movies WHERE tmdbId = ?), ?, ?, ?, ?, ?, ?, ?, ?)''',
'''INSERT OR IGNORE INTO table_movies(title, path, tmdbId, languages, subtitles,`hearing_impaired`, radarrId, overview, poster, fanart, `audio_language`, sceneName, monitored, sortTitle, year, alternativeTitles, format, resolution, video_codec, audio_codec, imdbId) VALUES (?,?,?,(SELECT languages FROM table_movies WHERE tmdbId = ?), '[]',(SELECT `hearing_impaired` FROM table_movies WHERE tmdbId = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
movies_to_add)
db.commit()
@ -183,5 +212,51 @@ def profile_id_to_language(id):
return profile[1]
def RadarrFormatAudioCodec(audioFormat, audioCodecID, audioProfile, audioAdditionalFeatures):
if audioFormat == "AC-3": return "AC3"
if audioFormat == "E-AC-3": return "EAC3"
if audioFormat == "AAC":
if audioCodecID == "A_AAC/MPEG4/LC/SBR":
return "HE-AAC"
else:
return "AAC"
if audioFormat.strip() == "mp3": return "MP3"
if audioFormat == "MPEG Audio":
if audioCodecID == "55" or audioCodecID == "A_MPEG/L3" or audioProfile == "Layer 3": return "MP3"
if audioCodecID == "A_MPEG/L2" or audioProfile == "Layer 2": return "MP2"
if audioFormat == "MLP FBA":
if audioAdditionalFeatures == "16-ch":
return "TrueHD Atmos"
else:
return "TrueHD"
return audioFormat
def RadarrFormatVideoCodec(videoFormat, videoCodecID, videoProfile, videoCodecLibrary):
if videoFormat == "x264": return "h264"
if videoFormat == "AVC" or videoFormat == "V.MPEG4/ISO/AVC": return "h264"
if videoFormat == "HEVC" or videoFormat == "V_MPEGH/ISO/HEVC":
if videoCodecLibrary.startswith("x265"): return "h265"
if videoFormat == "MPEG Video":
if videoCodecID == "2" or videoCodecID == "V_MPEG2":
return "Mpeg2"
else:
return "Mpeg"
if videoFormat == "MPEG-1 Video": return "Mpeg"
if videoFormat == "MPEG-2 Video": return "Mpeg2"
if videoFormat == "MPEG-4 Visual":
if videoCodecID.endswith("XVID") or videoCodecLibrary.startswith("XviD"): return "XviD"
if videoCodecID.endswith("DIV3") or videoCodecID.endswith("DIVX") or videoCodecID.endswith(
"DX50") or videoCodecLibrary.startswith("DivX"): return "DivX"
if videoFormat == "VC-1": return "VC1"
if videoFormat == "WMV2":
return "WMV"
if videoFormat == "DivX" or videoFormat == "div3":
return "DivX"
return videoFormat
if __name__ == '__main__':
update_movies()

@ -68,7 +68,10 @@ def update_series():
fanart = show['images'][0]['url'].split('?')[0]
except:
fanart = ""
if show['alternateTitles'] != None:
alternateTitles = str([item['title'] for item in show['alternateTitles']])
# Add shows in Sonarr to current shows list
current_shows_sonarr.append(show['tvdbId'])
@ -76,34 +79,36 @@ def update_series():
series_to_update.append((show["title"], show["path"], show["tvdbId"], show["id"], overview, poster,
fanart, profile_id_to_language(
(show['qualityProfileId'] if sonarr_version == 2 else show['languageProfileId'])),
show['sortTitle'], show["tvdbId"]))
show['sortTitle'], show['year'], alternateTitles, show["tvdbId"]))
else:
if serie_default_enabled is True:
series_to_add.append((show["title"], show["path"], show["tvdbId"], serie_default_language,
serie_default_hi, show["id"], overview, poster, fanart,
profile_id_to_language(show['qualityProfileId']), show['sortTitle']))
profile_id_to_language(show['qualityProfileId']), show['sortTitle'],
show['year'], alternateTitles))
else:
series_to_add.append((show["title"], show["path"], show["tvdbId"], show["tvdbId"],
show["tvdbId"], show["id"], overview, poster, fanart,
profile_id_to_language(show['qualityProfileId']), show['sortTitle']))
profile_id_to_language(show['qualityProfileId']), show['sortTitle'],
show['year'], alternateTitles))
# Update or insert series in DB
db = sqlite3.connect(os.path.join(args.config_dir, 'db', 'bazarr.db'), timeout=30)
c = db.cursor()
updated_result = c.executemany(
'''UPDATE table_shows SET title = ?, path = ?, tvdbId = ?, sonarrSeriesId = ?, overview = ?, poster = ?, fanart = ?, `audio_language` = ? , sortTitle = ? WHERE tvdbid = ?''',
'''UPDATE table_shows SET title = ?, path = ?, tvdbId = ?, sonarrSeriesId = ?, overview = ?, poster = ?, fanart = ?, `audio_language` = ? , sortTitle = ?, year = ?, alternateTitles = ? WHERE tvdbid = ?''',
series_to_update)
db.commit()
if serie_default_enabled is True:
added_result = c.executemany(
'''INSERT OR IGNORE INTO table_shows(title, path, tvdbId, languages,`hearing_impaired`, sonarrSeriesId, overview, poster, fanart, `audio_language`, sortTitle) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
'''INSERT OR IGNORE INTO table_shows(title, path, tvdbId, languages,`hearing_impaired`, sonarrSeriesId, overview, poster, fanart, `audio_language`, sortTitle, year, alternateTitles) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''',
series_to_add)
db.commit()
else:
added_result = c.executemany(
'''INSERT OR IGNORE INTO table_shows(title, path, tvdbId, languages,`hearing_impaired`, sonarrSeriesId, overview, poster, fanart, `audio_language`, sortTitle) VALUES (?,?,?,(SELECT languages FROM table_shows WHERE tvdbId = ?),(SELECT `hearing_impaired` FROM table_shows WHERE tvdbId = ?), ?, ?, ?, ?, ?, ?)''',
'''INSERT OR IGNORE INTO table_shows(title, path, tvdbId, languages,`hearing_impaired`, sonarrSeriesId, overview, poster, fanart, `audio_language`, sortTitle, year, alternateTitles) VALUES (?,?,?,(SELECT languages FROM table_shows WHERE tvdbId = ?),(SELECT `hearing_impaired` FROM table_shows WHERE tvdbId = ?), ?, ?, ?, ?, ?, ?, ?, ?)''',
series_to_add)
db.commit()
db.close()

@ -12,13 +12,15 @@ import cPickle as pickle
import codecs
import types
import chardet
import re
import subliminal
import subliminal_patch
from ast import literal_eval
from datetime import datetime, timedelta
from subzero.language import Language
from subzero.video import parse_video, refine_video
from subzero.video import parse_video
from subliminal import region, score as subliminal_scores, \
list_subtitles
list_subtitles, Episode, Movie
from subliminal_patch.core import SZAsyncProviderPool, download_best_subtitles, save_subtitles, download_subtitles
from subliminal_patch.score import compute_score
from get_languages import language_from_alpha3, alpha2_from_alpha3, alpha3_from_alpha2, language_from_alpha2
@ -64,12 +66,9 @@ def get_video(path, title, sceneName, use_scenename, providers=None, media_type=
video.used_scene_name = dont_use_actual_file
video.original_name = original_name
video.original_path = original_path
try:
refine_video(video)
except Exception as e:
logging.debug('BAZARR Error trying to refine this file: ' + path)
pass
refine_from_db(original_path,video)
return video
except:
logging.exception("BAZARR Error trying to get video information for this file: " + path)
else:
@ -104,7 +103,7 @@ def get_scores(video, media_type, min_score_movie_perc=60 * 100 / 120.0, min_sco
def download_subtitle(path, language, hi, providers, providers_auth, sceneName, title, media_type):
# fixme: supply all missing languages, not only one, to hit providers only once who support multiple languages in
# one query
logging.debug('BAZARR Searching subtitles for this file: ' + path)
if hi == "True":
hi = True
@ -237,7 +236,7 @@ def download_subtitle(path, language, hi, providers, providers_auth, sceneName,
def manual_search(path, language, hi, providers, providers_auth, sceneName, title, media_type):
logging.debug('BAZARR Manually searching subtitles for this file: ' + path)
final_subtitles = []
if hi == "True":
@ -616,3 +615,47 @@ def search_active(timestamp):
return False
else:
return True
def refine_from_db(path, video):
if isinstance(video, Episode):
db = sqlite3.connect(os.path.join(args.config_dir, 'db', 'bazarr.db'), timeout=30)
c = db.cursor()
data = c.execute("SELECT table_shows.title, table_episodes.season, table_episodes.episode, table_episodes.title, table_shows.year, table_shows.tvdbId, table_shows.alternateTitles, table_episodes.format, table_episodes.resolution, table_episodes.video_codec, table_episodes.audio_codec FROM table_episodes INNER JOIN table_shows on table_shows.sonarrSeriesId = table_episodes.sonarrSeriesId WHERE table_episodes.path = ?", (path_replace_reverse(path),)).fetchone()
db.close()
if data:
video.series = re.sub(r'(\(\d\d\d\d\))' , '', data[0])
video.season = int(data[1])
video.episode = int(data[2])
video.title = data[3]
if int(data[4]) > 0: video.year = int(data[4])
video.series_tvdb_id = int(data[5])
video.alternative_series = ast.literal_eval(data[6])
if not video.format:
video.format = str(data[7])
if not video.resolution:
video.resolution = str(data[8])
if not video.video_codec:
if data[9]: video.video_codec = data[9]
if not video.audio_codec:
if data[10]: video.audio_codec = data[10]
elif isinstance(video, Movie):
db = sqlite3.connect(os.path.join(args.config_dir, 'db', 'bazarr.db'), timeout=30)
c = db.cursor()
data = c.execute("SELECT title, year, alternativeTitles, format, resolution, video_codec, audio_codec, imdbId FROM table_movies WHERE path = ?", (path_replace_reverse_movie(path),)).fetchone()
db.close()
if data:
video.title = re.sub(r'(\(\d\d\d\d\))' , '', data[0])
if int(data[1]) > 0: video.year = int(data[1])
if data[7]: video.imdb_id = data[7]
video.alternative_titles = ast.literal_eval(data[2])
if not video.format:
if data[3]: video.format = data[3]
if not video.resolution:
if data[4]: video.resolution = data[4]
if not video.video_codec:
if data[5]: video.video_codec = data[5]
if not video.audio_codec:
if data[6]: video.audio_codec = data[6]
return video

@ -1,6 +1,9 @@
# coding=utf-8
bazarr_version = '0.7.1'
bazarr_version = '0.7.2'
from gevent import monkey
monkey.patch_all()
import gc
import sys

@ -95,5 +95,31 @@ if os.path.exists(os.path.join(args.config_dir, 'db', 'bazarr.db')):
else:
if settings.general.getboolean('use_radarr'):
execute_now('update_movies')
try:
c.execute('alter table table_shows add column "year" "text"')
c.execute('alter table table_shows add column "alternateTitles" "text"')
c.execute('alter table table_episodes add column "format" "text"')
c.execute('alter table table_episodes add column "resolution" "text"')
c.execute('alter table table_episodes add column "video_codec" "text"')
c.execute('alter table table_episodes add column "audio_codec" "text"')
c.execute('alter table table_movies add column "year" "text"')
c.execute('alter table table_movies add column "alternativeTitles" "text"')
c.execute('alter table table_movies add column "format" "text"')
c.execute('alter table table_movies add column "resolution" "text"')
c.execute('alter table table_movies add column "video_codec" "text"')
c.execute('alter table table_movies add column "audio_codec" "text"')
c.execute('alter table table_movies add column "imdbId" "text"')
db.commit()
except:
pass
else:
if settings.general.getboolean('use_sonarr'):
execute_now('update_series')
execute_now('sync_episodes')
if settings.general.getboolean('use_radarr'):
execute_now('update_movies')
db.close()

@ -73,12 +73,12 @@
active = search_active(lang[1])
if active:
%>
<a data-moviePath="{{row[3]}}" data-sceneName="{{row[5]}}" data-language="{{alpha3_from_alpha2(str(language))}}" data-hi="{{row[4]}}" data-radarrId={{row[2]}} class="get_subtitle ui tiny label">
<a data-moviePath="{{row[3]}}" data-sceneName="{{row[5]}}" data-language="{{alpha3_from_alpha2(str(language))}}" data-hi="{{row[4]}}" data-radarrId={{row[2]}} data-title="{{row[0].replace("'", "\'")}}" class="get_subtitle ui tiny label">
{{language}}
<i style="margin-left:3px; margin-right:0" class="search icon"></i>
</a>
%else:
<a data-tooltip="Automatic searching delayed (adaptive search)" data-position="top right" data-inverted="" data-moviePath="{{row[3]}}" data-sceneName="{{row[5]}}" data-language="{{alpha3_from_alpha2(str(language))}}" data-hi="{{row[4]}}" data-radarrId={{row[2]}} class="get_subtitle ui tiny label">
<a data-tooltip="Automatic searching delayed (adaptive search)" data-position="top right" data-inverted="" data-moviePath="{{row[3]}}" data-sceneName="{{row[5]}}" data-language="{{alpha3_from_alpha2(str(language))}}" data-hi="{{row[4]}}" data-radarrId={{row[2]}} data-title="{{row[0].replace("'", "\'")}}" class="get_subtitle ui tiny label">
{{language}}
<i style="margin-left:3px; margin-right:0" class="search red icon"></i>
</a>

@ -80,12 +80,12 @@
active = search_active(lang[1])
if active:
%>
<a data-episodePath="{{row[5]}}" data-sceneName="{{row[8]}}" data-language="{{alpha3_from_alpha2(str(language))}}" data-hi="{{row[6]}}" data-sonarrSeriesId={{row[4]}} data-sonarrEpisodeId={{row[7]}} class="get_subtitle ui tiny label">
<a data-episodePath="{{row[5]}}" data-sceneName="{{row[8]}}" data-language="{{alpha3_from_alpha2(str(language))}}" data-hi="{{row[6]}}" data-sonarrSeriesId={{row[4]}} data-sonarrEpisodeId={{row[7]}} data-title="{{row[0].replace("'", "\'")}}" class="get_subtitle ui tiny label">
{{language}}
<i style="margin-left:3px; margin-right:0" class="search icon"></i>
</a>
%else:
<a data-tooltip="Automatic searching delayed (adaptive search)" data-position="top right" data-inverted="" data-episodePath="{{row[5]}}" data-sceneName="{{row[8]}}" data-language="{{alpha3_from_alpha2(str(language))}}" data-hi="{{row[6]}}" data-sonarrSeriesId={{row[4]}} data-sonarrEpisodeId={{row[7]}} class="get_subtitle ui tiny label">
<a data-tooltip="Automatic searching delayed (adaptive search)" data-position="top right" data-inverted="" data-episodePath="{{row[5]}}" data-sceneName="{{row[8]}}" data-language="{{alpha3_from_alpha2(str(language))}}" data-hi="{{row[6]}}" data-sonarrSeriesId={{row[4]}} data-sonarrEpisodeId={{row[7]}} data-title="{{row[0].replace("'", "\'")}}" class="get_subtitle ui tiny label">
{{language}}
<i style="margin-left:3px; margin-right:0" class="search red icon"></i>
</a>

Loading…
Cancel
Save