Permalink
Browse files

initial commit (import from old songlocator repo)

  • Loading branch information...
0 parents commit 8e1282d69948c113ef34ab7cec82aa8b52af47e4 @andreypopp andreypopp committed Mar 10, 2013
Showing with 549 additions and 0 deletions.
  1. +15 −0 Makefile
  2. +1 −0 README
  3. +11 −0 component.json
  4. +21 −0 package.json
  5. +224 −0 songlocator-youtube.coffee
  6. +267 −0 songlocator-youtube.js
  7. +10 −0 songlocator-youtube.map
@@ -0,0 +1,15 @@
+SRC = $(shell find . -name '*.coffee' -type f)
+LIB = $(SRC:%.coffee=%.js)
+
+all: $(LIB)
+
+clean:
+ rm -f $(LIB)
+
+publish:
+ git push
+ git push --tags
+ npm publish
+
+%.js: %.coffee
+ coffee --map -bc $<
@@ -0,0 +1 @@
+SongLocator resovler for YouTube
@@ -0,0 +1,11 @@
+{
+ "name": "songlocator-youtube",
+ "version": "0.1.0",
+ "description": "SongLocator resolver for YouTube",
+ "tags": ["songlocator", "api", "music", "streaming", "audio", "resolver", "youtube"],
+ "author": "Andrey Popp <8mayday@gmail.com>",
+ "main": ["./songlocator-youtube.js"],
+ "dependencies": {
+ "songlocator-base": "~0.1.1"
+ }
+}
@@ -0,0 +1,21 @@
+{
+ "name": "songlocator-youtube",
+ "version": "0.1.0",
+ "description": "SongLocator resovler for YouTube",
+ "main": "songlocator-youtube.js",
+ "dependencies": {
+ "songlocator-base": "~0.1.1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/songlocator/songlocator-youtube.git"
+ },
+ "homepage": "https://github.com/songlocator/songlocator-youtube",
+ "keywords": ["songlocator", "api", "music", "streaming", "audio", "resolver", "youtube"],
+ "author": {
+ "name": "Andrey Popp",
+ "email": "8mayday@gmail.com",
+ "url": "http://andreypopp.com"
+ },
+ "license": "MIT"
+}
@@ -0,0 +1,224 @@
+###
+
+ SongLocator resolver for YouTube.
+
+ 2013 (c) Andrey Popp <8mayday@gmail.com>
+
+ Based on Tomahawk YouTube resolver.
+
+ 2012 (C) Hugo Lindström <hugolm84@gmail.com>
+ 2012 (C) Thierry Göckel <thierry@strayrayday.lu>
+ 2012 (C) Leo Franchi <lfranchi@kde.org>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+###
+
+((root, factory) ->
+ if typeof exports == 'object'
+ SongLocator = require('songlocator-base')
+ module.exports = factory(SongLocator)
+ else if typeof define == 'function' and define.amd
+ define (require) ->
+ SongLocator = require('songlocator-base')
+ root.SongLocator.YouTube = factory(SongLocator)
+ else
+ root.SongLocator.YouTube = factory(SongLocator)
+
+) this, ({BaseResolver, extend}) ->
+
+ regexIndexOf = (s, regex, startpos) ->
+ indexOf = s.substring(startpos || 0).search(regex)
+ return if indexOf >= 0 then indexOf + (startpos || 0) else indexOf
+
+ class Resolver extends BaseResolver
+ name: 'youtube'
+
+ search: (qid, query) ->
+ url = 'http://gdata.youtube.com/feeds/api/videos/'
+ params = {alt: 'jsonc', q: query, 'max-results': this.options.searchMaxResults, v: 2}
+ this.request
+ url: url
+ params: params
+ callback: (error, response) =>
+ return if error
+ return if response.data.totalItems == 0
+ results = for item in response.data.items
+ result = this.item2result(item, query)
+ continue unless result?
+ result
+ this.results(qid, results)
+
+ resolve: (qid, title, artist, album) ->
+ query = [artist or '', title or ''].join(' ').trim()
+ this.search(qid, query)
+
+ item2result: (item, query) ->
+ return unless item.title and item.duration and not item.contentRating
+ return unless this.dirtyCheckTitle(item.title, query)
+
+ parsedTrack = this.cleanupAndParseTrack(item.title, query)
+
+ return if not parsedTrack or not parsedTrack.artist?
+
+ return unless this.getTrack(item.title, query, true)
+
+ {
+ title: parsedTrack.track
+ artist: parsedTrack.artist
+ album: undefined
+
+ source: this.name
+ id: item.id
+
+ linkURL: item.player['default'] + '&hd=1'
+ imageURL: item.thumbnail?.hqDefault or item.thumbnail?.sqDefault
+ audioURL: undefined # we cannot know the direct link to the audio stream
+ audioPreviewURL: undefined
+
+ mimetype: "video/h264"
+ duration: item.duration
+ }
+
+ dirtyCheckTitle: (title, query) ->
+ # dirty check, filters out the most of the unwanted results
+ titleItem = title
+ .replace(/([^A-Za-z0-9\s])/gi, "")
+ .replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'')
+ .replace(/\s+/g,'|');
+ queryItem = query
+ .replace(/([^A-Za-z0-9\s])/gi, "")
+ .replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'')
+ .replace(/\s+/g,'|');
+ matches = titleItem.match(RegExp(queryItem, 'gi'))
+
+ matches and matches.length == queryItem.split("|").length
+
+ cleanupAndParseTrack: (title, query) ->
+ result = {}
+
+ # For the ease of parsing, remove these
+ # Maybe we could up the score a bit?
+ if regexIndexOf(title, /(?:[([](?=(official))).*?(?:[)\]])|(?:(official|video)).*?(?:(video))/i, 0 ) != -1
+ title = title.replace(/(?:[([](?=(official|video))).*?(?:[)\]])/gi, "")
+ title = title.replace(/(official|video(?:([!:-])))/gi, "")
+ result.isOfficial = 1
+
+ result.query = title
+
+ # Sometimes users separate titles with quotes :
+ # eg, "\"Young Forever\" Jay Z | Mr. Hudson (OFFICIAL VIDEO)"
+ # this will parse out the that title
+ inQuote = title.match(/([""'])(?:(?=(\\?))\2.).*\1/g);
+
+ if inQuote and inQuote != undefined
+ result.track = inQuote[0].substr(1, inQuote[0].length-2)
+ title = title.replace(inQuote[0],'')
+ result.fromQuote = result.track
+
+ result.parsed = this.parseCleanTrack( title )
+
+ if result.parsed
+ result.parsed.track = result.track
+ return result.parsed
+
+ else
+ result.parsed = this.parseCleanTrack(title)
+ if result.parsed
+ return result.parsed
+
+ # Still no luck, lets go deeper
+ if !result.parsed
+ if title.toLowerCase().indexOf(query.toLowerCase()) != -1
+ result.parsed = this.parseCleanTrack(title.replace(RegExp(query, "gi"), query.concat(" :")))
+ else
+ tryMatch = query.replace(/(?:[-|:&])/g, " ")
+ if title.toLowerCase().indexOf(tryMatch.toLowerCase()) != -1
+ replaceWith = if regexIndexOf(title, /(?:[-|:&])/g, 0) != -1
+ query
+ else
+ query.concat(" : ")
+ result.parsed = this.parseCleanTrack( title.replace(RegExp(tryMatch, "gi"), replaceWith))
+
+ if result.fromQuote and result.fromQuote != undefined
+ if result.parsed
+ result.artist = result.parsed.artist
+ result.track = result.fromQuote
+
+ else if result.parsed
+ if result.parsed.artist != undefined
+ result.artist = result.parsed.artist
+ if result.parsed.track != undefined
+ result.track = result.parsed.track
+
+ delete result.parsed
+ result
+
+ parseCleanTrack: (track) ->
+ result = {}
+ result.query = track
+ result.query.replace /.*?(?=([-:|]\s))/g, (param) ->
+ if param != ""
+ if result.artist == undefined
+ result.artist = param
+ else
+ if result.track == undefined
+ result.track = param
+
+ result.query.replace /(?=([-:|]\s)).*/g, (param) ->
+ if param != ""
+ if regexIndexOf(param, /([-|:]\s)/g, 0) == 0
+ if result.track == undefined
+ result.track = param.replace(/([-|:]\s)/g, "")
+ else
+ if tyresult.artist == undefined
+ result.artist = param
+ result.track = result.replace(/([-|:]\s)/g, "")
+
+ if result.track != undefined and result.artist != undefined
+ # Now, lets move featuring to track title, where it belongs
+ ftmatch = result.artist.match(/(?:(\s)(?=(feat.|feat|ft.|ft|featuring)(?=(\s)))).*/gi)
+ if ftmatch
+ result.artist = result.artist.replace(ftmatch, "")
+ result.track += " " + ftmatch
+
+ # Trim
+ result.track = result.track.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'').replace(/\s+/g,' ')
+ result.artist = result.artist.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'').replace(/\s+/g,' ')
+ return result
+
+ return
+
+ getTrack: (trackTitle, origTitle, isSearch) ->
+ if (this.options.includeCovers == false or this.options.includeCovers == undefined) \
+ and trackTitle.search(/(\Wcover(?!(\w)))/i) != -1 \
+ and origTitle.search(/(\Wcover(?!(\w)))/i) == -1
+ return
+
+ # Allow remix:es in search results?
+ if isSearch == undefined
+ if (this.options.includeRemixes == false or this.options.includeRemixes == undefined) \
+ and trackTitle.search(/(\W(re)*?mix(?!(\w)))/i) != -1 \
+ and origTitle.search(/(\W(re)*?mix(?!(\w)))/i) == -1
+ return
+
+ if (this.options.includeLive == false or this.options.includeLive == undefined) \
+ and trackTitle.search(/(live(?!(\w)))/i) != -1 \
+ and origTitle.search(/(live(?!(\w)))/i) == -1
+ return
+
+ else
+ return trackTitle
+
+ {Resolver}
Oops, something went wrong.

0 comments on commit 8e1282d

Please sign in to comment.