Skip to content
This repository has been archived by the owner on Aug 11, 2020. It is now read-only.

Add subtitle addon type #7

Merged
merged 1 commit into from
Jan 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion generators/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module.exports = yeoman.extend({
type: 'list',
name: 'type',
message: 'Choose the type of addon you want to create.',
choices: ['Contextmenu', 'Module', 'Plugin', 'Resource', 'Script', 'Service'],
choices: ['Contextmenu', 'Module', 'Plugin', 'Resource', 'Script', 'Service', 'Subtitle'],
default: 0
}];

Expand Down Expand Up @@ -94,6 +94,13 @@ module.exports = yeoman.extend({
message: 'Your addon id, it should be in the format script.name and not contain spaces. (for e.g. script.test.hello)',
validate: helper.validateScriptName
});
} else if (this.props.type == 'Subtitle') {
prompts.push({
type: 'input',
name: 'scriptid',
message: 'Your addon id, it should be in the format service.subtitles.name and not contain spaces. (for e.g. service.subtitles.hello)',
validate: helper.validateSubtitleName
});
}

prompts.push({
Expand Down Expand Up @@ -199,6 +206,11 @@ module.exports = yeoman.extend({
this.templatePath('service.py'),
this.destinationPath('service.py')
);
} else if (this.props.type == 'Subtitle') {
this.fs.copy(
this.templatePath('subtitle.py'),
this.destinationPath('subtitle.py')
);
}

if (this.props.type != 'Module' && this.props.type != 'Resource' && this.props.type != 'Contextmenu') {
Expand Down
2 changes: 1 addition & 1 deletion generators/app/templates/addon.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<provides><%= provides %></provides>
</extension><% } %><% if (props.type == 'Resource') {%><extension point="kodi.resource.images" compile="false" type="skinbackgrounds"/><% } %><% if (props.type == 'Script') {%><extension point="xbmc.python.script" library="script.py">
<provides><%= provides %></provides>
</extension><% } %><% if (props.type == 'Service') {%><extension point="xbmc.service" library="service.py" start="<%= props.start %>"/><% } %>
</extension><% } %><% if (props.type == 'Service') {%><extension point="xbmc.service" library="service.py" start="<%= props.start %>"/><% } %><% if (props.type == 'Subtitle') {%><extension point="xbmc.subtitle.module" library="subtitle.py" /><% } %>
<extension point="xbmc.addon.metadata">
<summary lang="en_GB"><%= props.summary %></summary>
<description lang="en_GB"></description>
Expand Down
151 changes: 151 additions & 0 deletions generators/app/templates/subtitle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-


import urllib2
import sys
import urlparse
import urllib
import os
import unicodedata
import xbmcgui
import xbmcplugin
import xbmc
import xbmcaddon
import xbmcvfs

ADDON = xbmcaddon.Addon()
SCRIPT_ID = ADDON.getAddonInfo('id')
PROFILE = xbmc.translatePath(ADDON.getAddonInfo('profile'))
TEMP = os.path.join(PROFILE, 'temp', '')
HANDLE = int(sys.argv[1])

if not xbmcvfs.exists(TEMP):
xbmcvfs.mkdirs(TEMP)


# function to retrieve parameters in a dictionary
def getParams():
if len(sys.argv) > 2:
return dict(urlparse.parse_qsl(sys.argv[2].lstrip('?')))
return {}


def normalizeString(str):
return unicodedata.normalize(
'NFKD', unicode(unicode(str, 'utf-8'))
).encode('ascii', 'ignore')


def getInfo():
item = {}
item['temp'] = False
item['rar'] = False
# Year
item['year'] = xbmc.getInfoLabel("VideoPlayer.Year")
# Season
item['season'] = str(xbmc.getInfoLabel("VideoPlayer.Season"))
# Episode
item['episode'] = str(xbmc.getInfoLabel("VideoPlayer.Episode"))
item['tvshow'] = normalizeString(
xbmc.getInfoLabel("VideoPlayer.TVshowtitle")) # Show
# try to get original title
item['title'] = normalizeString(
xbmc.getInfoLabel("VideoPlayer.OriginalTitle"))
item['file_original_path'] = urllib.unquote(
# Full path of a playing file
xbmc.Player().getPlayingFile().decode('utf-8'))

if item['title'] == "":
# no original title, get just Title
item['title'] = normalizeString(xbmc.getInfoLabel("VideoPlayer.Title"))

# Check if season is "Special"
if item['episode'].lower().find("s") > -1:
item['season'] = "0"
item['episode'] = item['episode'][-1:]

if (item['file_original_path'].find("http") > -1):
item['temp'] = True

elif (item['file_original_path'].find("rar://") > -1):
item['rar'] = True
item['file_original_path'] = os.path.dirname(
item['file_original_path'][6:])

elif (item['file_original_path'].find("stack://") > -1):
stackPath = item['file_original_path'].split(" , ")
item['file_original_path'] = stackPath[0][8:]

item['filename'] = os.path.splitext(
os.path.basename(item['file_original_path']))[0]
return item


def getLanguages(params):
langs = [] # ['scc','eng']
for lang in urllib.unquote(params['languages']).decode('utf-8').split(","):
langs.append(xbmc.convertLanguage(lang, xbmc.ISO_639_2))
return langs


def append_subtitle(subname, language, params, sync=False, h_impaired=False):
# languange long name (for example english)
listitem = xbmcgui.ListItem(label=xbmc.convertLanguage(language, xbmc.ENGLISH_NAME),
# subtitle name displayed
label2=subname,
# languange 2 letter name (for example en)
thumbnailImage=xbmc.convertLanguage(language, xbmc.ISO_639_1))

# subtitles synced with the video
listitem.setProperty("sync", 'true' if sync else 'false')
# hearing impaired subs
listitem.setProperty("hearing_imp", 'true' if h_impaired else 'false')

url = "plugin://{url}/?{params}".format(
url=SCRIPT_ID, params=urllib.urlencode(params))
xbmcplugin.addDirectoryItem(
handle=HANDLE, url=url, listitem=listitem, isFolder=False)


# add subtitles to the list from information kodi provides
def Search(info, languages):
append_subtitle(
"Lost 1x01", "eng", {"action": "download", "id": 15}, sync=True)
append_subtitle("Lost 1x01", "ita", {"action": "download", "id": 15})
append_subtitle("Lost 1x01 720p", "eng", {"action": "download", "id": 15})


# add subtitles to the list from manual user search
def ManualSearch(searchstr, languages):
append_subtitle(
"Lost 1x01", "eng", {"action": "download", "id": 15}, sync=True)
append_subtitle("Lost 1x01", "ita", {"action": "download", "id": 15})
append_subtitle("Lost 1x01 720p", "eng", {"action": "download", "id": 15})


# download the subtitle chosen by the user
def Download(params):
id = params['id']
# download the file requested
url = "http://path.to/subtitle/{id}.srt".format(id=id)
file = os.path.join(TEMP, "{id}.srt".format(id=id))

response = urllib2.urlopen(url)
with open(file, "w") as local_file:
local_file.write(response.read())

# give the file to kodi
xbmcplugin.addDirectoryItem(
handle=HANDLE, url=file, listitem=xbmcgui.ListItem(label=file), isFolder=False)

params = getParams()

if 'action' in params:
if params['action'] == "search":
Search(getInfo(), getLanguages(params))
elif params['action'] == "manualsearch":
ManualSearch(params['searchstring'], getLanguages(params))
elif params['action'] == "download":
Download(params)

xbmcplugin.endOfDirectory(HANDLE)
4 changes: 4 additions & 0 deletions generators/app/validationHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ helper.validateScriptName = function (str) {
return str.length > 'script.'.length;
};

helper.validateSubtitleName = function (str) {
return str.length > 'service.subtitles.'.length;
};

helper.validateScriptNameLength = function (str) {
return str.length > 2;
};
Expand Down
53 changes: 53 additions & 0 deletions test/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,56 @@ describe('generate service', function () {
assert.fileContent('addon.xml', '<import addon="xbmc.python" version="2.25.0"/>');
});
});

describe('generate subtitle', function () {
before(function () {
return helpers.run(path.join(__dirname, '../generators/app'))
.withPrompts({
type: 'Subtitle',
scriptid: 'subtitle.test',
scriptname: 'My subtitle name',
kodiVersion: '2.25.0',
platforms: 'all',
license: 'MIT',
authors: 'Me',
summary: 'My summary',
authorName: 'My real name',
email: 'test@test.de',
website: 'www.kodi.tv'
})
.toPromise();
});

it('creates subtitle files', function () {
assert.file([
'addon.xml',
'.gitignore',
'changelog.txt',
'README.md',
'subtitle.py',
'tests/README.md',
'resources/__init__.py',
'resources/language/resource.language.en_gb/strings.po',
'resources/language/README.md',
'resources/lib/__init__.py',
'resources/lib/README.md',
'resources/settings.xml',
'LICENSE'
]);

assert.noFile([
'context.py',
'plugin.py',
'script.py',
'service.py'
]);
});

it('check service addon.xml content', function () {
assert.fileContent('addon.xml', '<addon id="subtitle.test" ');
assert.fileContent('addon.xml', '<extension point="xbmc.subtitle.module" library="subtitle.py" />');
assert.fileContent('addon.xml', ' provider-name="Me">');
assert.fileContent('addon.xml', '<platform>all</platform>');
assert.fileContent('addon.xml', '<import addon="xbmc.python" version="2.25.0"/>');
});
});
8 changes: 8 additions & 0 deletions test/validationHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ describe('prompting validations → validateScriptName()', () => {
assert.equal(helper.validateScriptName('script'), false);
});
});
describe('prompting validations → validateSubtitleName()', () => {
it('should work for service.subtitles.test', () => {
assert.equal(helper.validateSubtitleName('service.subtitles.test'), true);
});
it('should fail when to short', () => {
assert.equal(helper.validateSubtitleName('service.subtitles'), false);
});
});
describe('prompting validations → validateScriptNameLength()', () => {
it('should work for anything longer than two letters', () => {
assert.equal(helper.validateScriptNameLength('My name'), true);
Expand Down