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

Commit

Permalink
Add subtitle addon type
Browse files Browse the repository at this point in the history
  • Loading branch information
razzeee committed Jan 19, 2017
1 parent 761f4d7 commit 53f7d0e
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 2 deletions.
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

0 comments on commit 53f7d0e

Please sign in to comment.