diff --git a/.gitignore b/.gitignore index 755af6d..d105801 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ node_modules # Narration /html/static/media/cant_think_suricrasia_online.mp3 +/html/transparency diff --git a/api/takedown.js b/api/takedown.js new file mode 100644 index 0000000..f9115b3 --- /dev/null +++ b/api/takedown.js @@ -0,0 +1,10 @@ +const constants = require("../utils/constants") +const {render} = require("pinski/plugins") + +module.exports = [ + { + route: "/takedown", methods: ["GET"], code: async () => { + return render(200, "pug/takedown.pug", {constants}) + } + } +] diff --git a/api/video.js b/api/video.js index 2f613ad..1161fd5 100644 --- a/api/video.js +++ b/api/video.js @@ -108,6 +108,12 @@ module.exports = [ const settings = user.getSettingsOrDefaults() const id = url.searchParams.get("v") + // Check if playback is allowed + const videoTakedownInfo = db.prepare("SELECT id, org, url FROM TakedownVideos WHERE id = ?").get(id) + if (videoTakedownInfo) { + return render(451, "pug/takedown-video.pug", videoTakedownInfo) + } + // Media fragment const t = url.searchParams.get("t") let mediaFragment = converters.tToMediaFragment(t) @@ -141,6 +147,15 @@ module.exports = [ if (!video) throw new MessageError("The instance returned null.") if (video.error) throw new InstanceError(video.error, video.identifier) + // Check if channel playback is allowed + const channelTakedownInfo = db.prepare("SELECT ucid, org, url FROM TakedownChannels WHERE ucid = ?").get(video.authorId) + if (channelTakedownInfo) { + // automatically add the entry to the videos list, so it won't be fetched again + const args = {id, ...channelTakedownInfo} + db.prepare("INSERT INTO TakedownVideos (id, org, url) VALUES (@id, @org, @url)").run(args) + return render(451, "pug/takedown-video.pug", channelTakedownInfo) + } + // process stream list ordering const formats = sortFormats(video, settings.quality) diff --git a/pug/includes/layout.pug b/pug/includes/layout.pug index 5da074d..62504ae 100644 --- a/pug/includes/layout.pug +++ b/pug/includes/layout.pug @@ -34,7 +34,7 @@ html p Released as AGPL free software. .footer__cols div - h3.footer__colhead Source + h3.footer__colhead Source code ul.footer__list li: a(href="https://sr.ht/~cadence/tube") Project hub li: a(href="https://lists.sr.ht/~cadence/tube-announce") Announcements @@ -44,3 +44,5 @@ html ul.footer__list li: a(href="/privacy") Privacy policy li: a(href="/licenses" data-jslicense=1) Licenses + if constants.takedown + li: a(href="/takedown") DMCA diff --git a/pug/takedown-video.pug b/pug/takedown-video.pug new file mode 100644 index 0000000..e72224d --- /dev/null +++ b/pug/takedown-video.pug @@ -0,0 +1,22 @@ +extends includes/layout + +include includes/video-list-item +include includes/subscribe-button + +block head + title Unavailable for legal reasons - CloudTube + +block content + main.video-error-page + h2 Unavailable for legal reasons + p The copyright holders of this video issued a legal notice directly to this website. They demanded that access to this particular video, or the particular channel it's from, should be forbidden. + if org + p The notice was issued by #{org}. + if url + p: a(href=url) More information about the notice is available here. + p Any content not covered by this notice is still available. + p You can still #[a(href=`https://www.youtube.com/watch?v=${id}#cloudtube`) watch the video on YouTube.] + footer.takedown-footer + p: i Let this be a warning of why you shouldn't rely on centralised services! + p: i You will always be able to download the CloudTube source code, or try another instance. + p: i That's all we know. diff --git a/pug/takedown.pug b/pug/takedown.pug new file mode 100644 index 0000000..f9b343b --- /dev/null +++ b/pug/takedown.pug @@ -0,0 +1,33 @@ +extends includes/layout + +block head + title Takedown - CloudTube + +block content + main.takedown-page + h1 Takedown/DMCA + p If you own the copyright to videos on this website, you can remove access to the videos. But: + section.important-section + h2 Read this first. + ul + li CloudTube is a proxy service for YouTube videos. + li CloudTube does not store any videos. Videos are never saved to these servers. + li CloudTube reproduces data from youtube.com in an interface that looks a little bit different. + li All video data is streamed directly from YouTube. All video metadata is collected directly from YouTube. + h2 And understand this: + p Anybody can watch or download YouTube videos using many freely available tools. This is just one such tool, and attacking this website will solve none of your problems. + h2.new-section “I still want to remove access.” + p In addition to the legally mandated information, if you wish for future videos uploaded on the same channel(s) to be restricted, please provide the channel URLs, and state: "Future videos uploaded to the same YouTube channels should also be restricted." + p When providing URLs, please format them like so: + pre. + https://youtube.example.com/watch?v=AAAAAAAAAAA + https://youtube.example.com/watch?v=BBBBBBBBBBB + p i.e., one URL per line, with no other information on those lines. + section.takedown-contact + if constants.takedown && constants.takedown.contact_url + p: a(href=constants.takedown.contact_url) Contact information is here. + else if constants.takedown && constants.takedown.contact_email + p: a(href=`mailto:${constants.takedown.contact_email}`) Send email to #{constants.takedown.contact_email} + else + p You will need to contact the website owner via their domain or hosting provider. + p Please allow 5 days for a response. diff --git a/sass/includes/takedown-page.sass b/sass/includes/takedown-page.sass new file mode 100644 index 0000000..36ede5d --- /dev/null +++ b/sass/includes/takedown-page.sass @@ -0,0 +1,14 @@ +@use "colors.sass" as c + +.takedown-page + max-width: 700px + margin: 0 auto + + .new-section + margin-top: 200px + + .important-section + padding: 4px 20px + border: 1px solid c.$edge-grey + color: c.$fg-bright + background-color: c.$bg-darker diff --git a/sass/includes/video-page.sass b/sass/includes/video-page.sass index 76b6ea3..964dc77 100644 --- a/sass/includes/video-page.sass +++ b/sass/includes/video-page.sass @@ -161,6 +161,9 @@ .actor margin: 4px 0px 10px +.takedown-footer + margin-top: 80px + // Chapter highlights .timestamp--active diff --git a/sass/main.sass b/sass/main.sass index a912132..17c15df 100644 --- a/sass/main.sass +++ b/sass/main.sass @@ -1,5 +1,6 @@ -@use "includes/base.sass" @use "includes/colors.sass" as c + +@use "includes/base.sass" @use "sass:selector" @use "includes/video-page.sass" @use "includes/search-page.sass" @@ -11,6 +12,7 @@ @use "includes/privacy-page.sass" @use "includes/licenses-page.sass" @use "includes/filters-page.sass" +@use "includes/takedown-page.sass" @use "includes/forms.sass" @use "includes/nav.sass" @use "includes/footer.sass" diff --git a/utils/upgradedb.js b/utils/upgradedb.js index e12ae2d..e5f65e5 100644 --- a/utils/upgradedb.js +++ b/utils/upgradedb.js @@ -63,6 +63,13 @@ const deltas = [ function() { db.prepare("CREATE INDEX Videos_authorID ON Videos (authorID)") .run() + }, + // 10: +TakedownVideos, +TakedownChannels + function() { + db.prepare("CREATE TABLE TakedownVideos (id TEXT NOT NULL, org TEXT, url TEXT, PRIMARY KEY (id))") + .run() + db.prepare("CREATE TABLE TakedownChannels (ucid TEXT NOT NULL, org TEXT, url TEXT, PRIMARY KEY (ucid))") + .run() } ]