From 8da1d1c772e7ed985bb2f51fd31b8263156f4ca3 Mon Sep 17 00:00:00 2001 From: Xaekai Date: Wed, 26 Jan 2022 20:33:59 -0800 Subject: [PATCH] Add BandCamp support --- bin/build-player.js | 2 +- .../{bitchute.coffee => iframechild.coffee} | 7 ++++--- player/update.coffee | 3 ++- src/custom-media.js | 19 ++++++++++++++++++- src/get-info.js | 10 ++++++++++ src/media.js | 5 +++++ src/utilities.js | 3 +++ www/js/util.js | 9 +++++++++ 8 files changed, 52 insertions(+), 6 deletions(-) rename player/{bitchute.coffee => iframechild.coffee} (90%) diff --git a/bin/build-player.js b/bin/build-player.js index c82605fe..bf438866 100755 --- a/bin/build-player.js +++ b/bin/build-player.js @@ -23,7 +23,7 @@ var order = [ 'hls.coffee', 'twitchclip.coffee', 'peertube.coffee', - 'bitchute.coffee', + 'iframechild.coffee', 'update.coffee' ]; diff --git a/player/bitchute.coffee b/player/iframechild.coffee similarity index 90% rename from player/bitchute.coffee rename to player/iframechild.coffee index 4a85139a..1d46b9aa 100644 --- a/player/bitchute.coffee +++ b/player/iframechild.coffee @@ -1,7 +1,7 @@ -window.BitChutePlayer = class BitChutePlayer extends Player +window.IframeChild = class IframeChild extends Player constructor: (data) -> - if not (this instanceof BitChutePlayer) - return new BitChutePlayer(data) + if not (this instanceof IframeChild) + return new IframeChild(data) @load(data) @@ -57,6 +57,7 @@ window.BitChutePlayer = class BitChutePlayer extends Player iframe.contentWindow.document.querySelector('#ytapiplayer').classList.add('vjs-fluid') adapter = iframe.contentWindow.playerjs.VideoJSAdapter(iframe.contentWindow.PLAYER.player) adapter.ready() + typeof data?.meta?.thumbnail == 'string' and iframe.contentWindow.PLAYER.player.poster(data.meta.thumbnail) ) play: -> diff --git a/player/update.coffee b/player/update.coffee index de86e825..1586c6af 100644 --- a/player/update.coffee +++ b/player/update.coffee @@ -14,8 +14,9 @@ TYPE_MAP = sb: StreamablePlayer tc: TwitchClipPlayer cm: VideoJSPlayer - bc: BitChutePlayer pt: PeerPlayer + bc: IframeChild + bn: IframeChild window.loadMediaPlayer = (data) -> try diff --git a/src/custom-media.js b/src/custom-media.js index 88c202ab..53c87572 100644 --- a/src/custom-media.js +++ b/src/custom-media.js @@ -30,6 +30,17 @@ const SOURCE_CONTENT_TYPES = new Set([ 'video/webm' ]); +const LIVE_ONLY_CONTENT_TYPES = new Set([ + 'application/dash+xml' +]); + +const AUDIO_ONLY_CONTENT_TYPES = new Set([ + 'audio/aac', + 'audio/ogg', + 'audio/mpeg', + 'audio/opus' +]); + export function lookup(url, opts) { if (!opts) opts = {}; if (!opts.hasOwnProperty('timeout')) opts.timeout = 10000; @@ -179,7 +190,13 @@ function validateSources(sources) { `unacceptable source contentType "${source.contentType}"` ); - if (!SOURCE_QUALITIES.has(source.quality)) + if (LIVE_ONLY_CONTENT_TYPES.has(source.contentType) && !data.live) + throw new ValidationError( + `contentType "${source.contentType}" requires live: true` + ); + + // TODO: This should be allowed + if (/*!AUDIO_ONLY_CONTENT_TYPES.has(source.contentType) && */!SOURCE_QUALITIES.has(source.quality)) throw new ValidationError(`unacceptable source quality "${source.quality}"`); if (source.hasOwnProperty('bitrate')) { diff --git a/src/get-info.js b/src/get-info.js index 74e798f1..5d2c0f89 100644 --- a/src/get-info.js +++ b/src/get-info.js @@ -8,6 +8,7 @@ const YouTube = require("@cytube/mediaquery/lib/provider/youtube"); const Vimeo = require("@cytube/mediaquery/lib/provider/vimeo"); const PeerTube = require("@cytube/mediaquery/lib/provider/peertube"); const BitChute = require("@cytube/mediaquery/lib/provider/bitchute"); +const BandCamp = require("@cytube/mediaquery/lib/provider/bandcamp"); const Streamable = require("@cytube/mediaquery/lib/provider/streamable"); const TwitchVOD = require("@cytube/mediaquery/lib/provider/twitch-vod"); const TwitchClip = require("@cytube/mediaquery/lib/provider/twitch-clip"); @@ -396,6 +397,15 @@ var Getters = { }); }, + /* BandCamp */ + bn: function (id, callback) { + BandCamp.lookup(id).then(video => { + video = new Media(video.id, video.title, video.duration, "bn", video.meta); + callback(null, video); + }).catch(error => { + callback(error.message || error); + }); + } }; module.exports = { diff --git a/src/media.js b/src/media.js index 40c0dd12..f486a853 100644 --- a/src/media.js +++ b/src/media.js @@ -53,6 +53,11 @@ Media.prototype = { result.meta.direct = this.meta.direct; } + // Only save thumbnails for items which can be audio track only + if (['bn','cm'].includes(this.type)) { + result.meta.thumbnail = this.meta.thumbnail; + } + return result; }, diff --git a/src/utilities.js b/src/utilities.js index acf9ea25..42c11f2b 100644 --- a/src/utilities.js +++ b/src/utilities.js @@ -210,6 +210,9 @@ return `https://${domain}/videos/watch/${uuid}`; case "bc": return `https://www.bitchute.com/video/${id}/`; + case "bn": + const [artist,track] = id.split(';'); + return `https://${artist}.bandcamp.com/track/${track}`; default: return ""; } diff --git a/www/js/util.js b/www/js/util.js index dbb40f9b..f3e9c5e4 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -61,6 +61,9 @@ function formatURL(data) { } case "bc": return `https://www.bitchute.com/video/${data.id}/`; + case "bn": + const [artist,track] = data.id.split(';'); + return `https://${artist}.bandcamp.com/track/${track}`; default: return "#"; } @@ -1402,6 +1405,12 @@ function parseMediaLink(url) { } + if(data.hostname.endsWith('.bandcamp.com') && data.pathname.startsWith('/track/')){ + const artist = data.hostname.replace('.bandcamp.com','') + const track = data.pathname.replace('/track/','') + return { type: 'bn', id: `${artist};${track}` } + } + /* PeerTubes */ if(data.pathname.match('^/w/|^/videos/watch/')){ const regLong = [8, 4, 4, 4, 12].map(x => `[0-9a-f]{${x}}`).join('-');