b9f703086f070fa72a89bf0212478776cd26e9e2 — Cadence Ember 14 days ago 741a119 main
Implement takedown system
M .gitignore => .gitignore +1 -0
@@ 14,3 14,4 @@ node_modules

# Narration

A api/takedown.js => api/takedown.js +10 -0
@@ 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})

M api/video.js => api/video.js +15 -0
@@ 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)

M pug/includes/layout.pug => pug/includes/layout.pug +3 -1
@@ 34,7 34,7 @@ html
          p Released as AGPL free software.
              h3.footer__colhead Source
              h3.footer__colhead Source code
                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
                li: a(href="/privacy") Privacy policy
                li: a(href="/licenses" data-jslicense=1) Licenses
                if constants.takedown
                  li: a(href="/takedown") DMCA

A pug/takedown-video.pug => pug/takedown-video.pug +22 -0
@@ 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
    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.]
      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.

A pug/takedown.pug => pug/takedown.pug +33 -0
@@ 0,0 1,33 @@
extends includes/layout

block head
  title Takedown - CloudTube

block content
    h1 Takedown/DMCA
    p If you own the copyright to videos on this website, you can remove access to the videos. But:
      h2 Read this first.
        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:
    p i.e., one URL per line, with no other information on those lines.
      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}
        p You will need to contact the website owner via their domain or hosting provider.
    p Please allow 5 days for a response.

A sass/includes/takedown-page.sass => sass/includes/takedown-page.sass +14 -0
@@ 0,0 1,14 @@
@use "colors.sass" as c

  max-width: 700px
  margin: 0 auto

    margin-top: 200px

    padding: 4px 20px
    border: 1px solid c.$edge-grey
    color: c.$fg-bright
    background-color: c.$bg-darker

M sass/includes/video-page.sass => sass/includes/video-page.sass +3 -0
@@ 161,6 161,9 @@
        margin: 4px 0px 10px

  margin-top: 80px

// Chapter highlights


M sass/main.sass => sass/main.sass +3 -1
@@ 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"

M utils/upgradedb.js => utils/upgradedb.js +7 -0
@@ 63,6 63,13 @@ const deltas = [
	function() {
		db.prepare("CREATE INDEX Videos_authorID ON Videos (authorID)")
	// 10: +TakedownVideos, +TakedownChannels
	function() {
		db.prepare("CREATE TABLE TakedownVideos (id TEXT NOT NULL, org TEXT, url TEXT, PRIMARY KEY (id))")
		db.prepare("CREATE TABLE TakedownChannels (ucid TEXT NOT NULL, org TEXT, url TEXT, PRIMARY KEY (ucid))")