forked from midou/invidious
		
	Use seperate table for videos pulled from RSS
This commit is contained in:
		
							
								
								
									
										20
									
								
								config/sql/channel_videos.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								config/sql/channel_videos.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
-- Table: public.channel_videos
 | 
			
		||||
 | 
			
		||||
-- DROP TABLE public.channel_videos;
 | 
			
		||||
 | 
			
		||||
CREATE TABLE public.channel_videos
 | 
			
		||||
(
 | 
			
		||||
    id text COLLATE pg_catalog."default" NOT NULL,
 | 
			
		||||
    title text COLLATE pg_catalog."default",
 | 
			
		||||
    published timestamp with time zone,
 | 
			
		||||
    updated timestamp with time zone,
 | 
			
		||||
    ucid text COLLATE pg_catalog."default",
 | 
			
		||||
    author text COLLATE pg_catalog."default",
 | 
			
		||||
    CONSTRAINT channel_videos_id_key UNIQUE (id)
 | 
			
		||||
)
 | 
			
		||||
WITH (
 | 
			
		||||
    OIDS = FALSE
 | 
			
		||||
)
 | 
			
		||||
TABLESPACE pg_default;
 | 
			
		||||
 | 
			
		||||
GRANT ALL ON TABLE public.channel_videos TO kemal;
 | 
			
		||||
@@ -5,9 +5,8 @@
 | 
			
		||||
CREATE TABLE public.channels
 | 
			
		||||
(
 | 
			
		||||
    id text COLLATE pg_catalog."default" NOT NULL,
 | 
			
		||||
    rss text COLLATE pg_catalog."default",
 | 
			
		||||
    updated timestamp with time zone,
 | 
			
		||||
    author text COLLATE pg_catalog."default"
 | 
			
		||||
    author text COLLATE pg_catalog."default",
 | 
			
		||||
    updated timestamp with time zone
 | 
			
		||||
)
 | 
			
		||||
WITH (
 | 
			
		||||
    OIDS = FALSE
 | 
			
		||||
@@ -15,12 +14,3 @@ WITH (
 | 
			
		||||
TABLESPACE pg_default;
 | 
			
		||||
 | 
			
		||||
GRANT ALL ON TABLE public.channels TO kemal;
 | 
			
		||||
 | 
			
		||||
-- Index: channel_id_idx
 | 
			
		||||
 | 
			
		||||
-- DROP INDEX public.channel_id_idx;
 | 
			
		||||
 | 
			
		||||
CREATE UNIQUE INDEX channel_id_idx
 | 
			
		||||
    ON public.channels USING btree
 | 
			
		||||
    (id COLLATE pg_catalog."default")
 | 
			
		||||
    TABLESPACE pg_default;
 | 
			
		||||
							
								
								
									
										1
									
								
								setup.sh
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								setup.sh
									
									
									
									
									
								
							@@ -4,3 +4,4 @@ createdb invidious
 | 
			
		||||
createuser kemal
 | 
			
		||||
psql invidious < config/sql/channels.sql
 | 
			
		||||
psql invidious < config/sql/videos.sql
 | 
			
		||||
psql invidious < config/sql/channel_videos.sql
 | 
			
		||||
 
 | 
			
		||||
@@ -62,15 +62,20 @@ class InvidiousChannel
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  add_mapping({
 | 
			
		||||
    id:  String,
 | 
			
		||||
    rss: {
 | 
			
		||||
      type:      XML::Node,
 | 
			
		||||
      default:   XML.parse_html(""),
 | 
			
		||||
      converter: InvidiousChannel::XMLConverter,
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
    updated: Time,
 | 
			
		||||
    id:      String,
 | 
			
		||||
    author:  String,
 | 
			
		||||
    updated: Time,
 | 
			
		||||
  })
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class ChannelVideo
 | 
			
		||||
  add_mapping({
 | 
			
		||||
    id:        String,
 | 
			
		||||
    title:     String,
 | 
			
		||||
    published: Time,
 | 
			
		||||
    updated:   Time,
 | 
			
		||||
    ucid:      String,
 | 
			
		||||
    author:    String,
 | 
			
		||||
  })
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@@ -203,10 +208,16 @@ def get_video(id, client, db, refresh = true)
 | 
			
		||||
 | 
			
		||||
    # If record was last updated over an hour ago, refresh (expire param in response lasts for 6 hours)
 | 
			
		||||
    if refresh && Time.now - video.updated > 1.hours
 | 
			
		||||
      db.exec("DELETE FROM videos * WHERE id = $1", id)
 | 
			
		||||
      begin
 | 
			
		||||
      video = fetch_video(id, client)
 | 
			
		||||
      args = arg_array(video.to_a)
 | 
			
		||||
      db.exec("INSERT INTO videos VALUES (#{args})", video.to_a)
 | 
			
		||||
        video_array = video.to_a[1..-1]
 | 
			
		||||
        args = arg_array(video_array)
 | 
			
		||||
 | 
			
		||||
        db.exec("UPDATE videos SET (id,info,updated,title,views,likes,dislikes,wilson_score,published,description,language)\
 | 
			
		||||
        = (#{args}) WHERE id = '#{video.id}'", video_array)
 | 
			
		||||
      rescue ex
 | 
			
		||||
        db.exec("DELETE FROM videos * WHERE id = $1", id)
 | 
			
		||||
    end
 | 
			
		||||
    end
 | 
			
		||||
  else
 | 
			
		||||
    video = fetch_video(id, client)
 | 
			
		||||
@@ -490,14 +501,14 @@ def get_channel(id, client, db)
 | 
			
		||||
    channel = db.query_one("SELECT * FROM channels WHERE id = $1", id, as: InvidiousChannel)
 | 
			
		||||
 | 
			
		||||
    if Time.now - channel.updated > 1.minutes
 | 
			
		||||
      channel = fetch_channel(id, client)
 | 
			
		||||
      channel = fetch_channel(id, client, db)
 | 
			
		||||
      channel_array = channel.to_a[1..-1]
 | 
			
		||||
      args = arg_array(channel_array)
 | 
			
		||||
 | 
			
		||||
      db.exec("UPDATE channels SET (rss,updated,author) = (#{args}) WHERE id = '#{channel.id}'", channel_array)
 | 
			
		||||
      db.exec("UPDATE channels SET (author,updated) = (#{args}) WHERE id = '#{channel.id}'", channel_array)
 | 
			
		||||
    end
 | 
			
		||||
  else
 | 
			
		||||
    channel = fetch_channel(id, client)
 | 
			
		||||
    channel = fetch_channel(id, client, db)
 | 
			
		||||
    args = arg_array(channel.to_a)
 | 
			
		||||
    db.exec("INSERT INTO channels VALUES (#{args})", channel.to_a)
 | 
			
		||||
  end
 | 
			
		||||
@@ -505,13 +516,31 @@ def get_channel(id, client, db)
 | 
			
		||||
  return channel
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def fetch_channel(id, client)
 | 
			
		||||
def fetch_channel(id, client, db)
 | 
			
		||||
  rss = client.get("/feeds/videos.xml?channel_id=#{id}").body
 | 
			
		||||
  rss = XML.parse_html(rss)
 | 
			
		||||
 | 
			
		||||
  rss.xpath_nodes("//feed/entry").each do |entry|
 | 
			
		||||
    video_id = entry.xpath_node("videoid").not_nil!.content
 | 
			
		||||
    title = entry.xpath_node("title").not_nil!.content
 | 
			
		||||
    published = Time.parse(entry.xpath_node("published").not_nil!.content, "%FT%X%z")
 | 
			
		||||
    updated = Time.parse(entry.xpath_node("updated").not_nil!.content, "%FT%X%z")
 | 
			
		||||
    author = entry.xpath_node("author/name").not_nil!.content
 | 
			
		||||
    ucid = entry.xpath_node("channelid").not_nil!.content
 | 
			
		||||
 | 
			
		||||
    video = ChannelVideo.new(video_id, title, published, updated, ucid, author)
 | 
			
		||||
 | 
			
		||||
    video_array = video.to_a[1..-1]
 | 
			
		||||
    args = arg_array(video_array)
 | 
			
		||||
 | 
			
		||||
    # TODO: Update record on conflict
 | 
			
		||||
    db.exec("INSERT INTO channel_videos VALUES (#{arg_array(video.to_a)})\
 | 
			
		||||
      ON CONFLICT (id) DO NOTHING", video.to_a)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  author = rss.xpath_node("//feed/author/name").not_nil!.content
 | 
			
		||||
 | 
			
		||||
  channel = InvidiousChannel.new(id, rss, Time.now, author)
 | 
			
		||||
  channel = InvidiousChannel.new(id, author, Time.now)
 | 
			
		||||
 | 
			
		||||
  return channel
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -574,35 +574,22 @@ get "/feed/subscriptions" do |env|
 | 
			
		||||
 | 
			
		||||
    feed = client.get("/subscription_manager?action_takeout=1", headers).body
 | 
			
		||||
 | 
			
		||||
    videos = Array(Hash(String, String | Time)).new
 | 
			
		||||
    channels = [] of String
 | 
			
		||||
 | 
			
		||||
    feed = XML.parse_html(feed)
 | 
			
		||||
    feed.xpath_nodes("//opml/outline/outline").each do |channel|
 | 
			
		||||
      id = channel["xmlurl"][-24..-1]
 | 
			
		||||
      rss = get_channel(id, client, PG_DB).rss
 | 
			
		||||
      get_channel(id, client, PG_DB)
 | 
			
		||||
 | 
			
		||||
      rss.xpath_nodes("//feed/entry").each do |entry|
 | 
			
		||||
        video = {} of String => String | Time
 | 
			
		||||
 | 
			
		||||
        video["id"] = entry.xpath_node("videoid").not_nil!.content
 | 
			
		||||
        video["title"] = entry.xpath_node("title").not_nil!.content
 | 
			
		||||
        video["published"] = Time.parse(entry.xpath_node("published").not_nil!.content, "%FT%X%z")
 | 
			
		||||
        video["author"] = entry.xpath_node("author/name").not_nil!.content
 | 
			
		||||
        video["ucid"] = entry.xpath_node("channelid").not_nil!.content
 | 
			
		||||
        video["thumbnail"] = entry.xpath_node("group/thumbnail").not_nil!["url"].gsub(/hqdefault\.jpg$/, "mqdefault.jpg")
 | 
			
		||||
 | 
			
		||||
        videos << video
 | 
			
		||||
      end
 | 
			
		||||
      channels << id
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    youtube_pool << client
 | 
			
		||||
 | 
			
		||||
    videos.sort_by! { |video| video["published"].as(Time).epoch }
 | 
			
		||||
    videos.reverse!
 | 
			
		||||
 | 
			
		||||
    start = (page - 1)*max_results
 | 
			
		||||
    stop = start + max_results - 1
 | 
			
		||||
    videos = videos[start..stop]
 | 
			
		||||
    time = Time.now
 | 
			
		||||
    args = arg_array(channels)
 | 
			
		||||
    offset = (page - 1) * max_results
 | 
			
		||||
    videos = PG_DB.query_all("SELECT * FROM channel_videos WHERE ucid IN (#{args})\
 | 
			
		||||
      ORDER BY published DESC LIMIT #{max_results} OFFSET #{offset}", channels, as: ChannelVideo)
 | 
			
		||||
 | 
			
		||||
    templated "subscriptions"
 | 
			
		||||
  else
 | 
			
		||||
 
 | 
			
		||||
@@ -7,15 +7,15 @@
 | 
			
		||||
    <% slice.each do |video| %>
 | 
			
		||||
    <div class="pure-u-1 pure-u-md-1-4">
 | 
			
		||||
        <div style="overflow-wrap:break-word; word-wrap:break-word;" class="h-box">
 | 
			
		||||
            <a style="width:100%;" href="/watch?v=<%= video["id"] %>">
 | 
			
		||||
                <img style="width:100%;" src="<%= video["thumbnail"] %>"/>
 | 
			
		||||
                <p style="height:100%"><%= video["title"] %></p>
 | 
			
		||||
            <a style="width:100%;" href="/watch?v=<%= video.id %>">
 | 
			
		||||
                <img style="width:100%;" src="https://i.ytimg.com/vi/<%= video.id %>/mqdefault.jpg"/>
 | 
			
		||||
                <p style="height:100%"><%= video.title %></p>
 | 
			
		||||
            </a>
 | 
			
		||||
            <p>
 | 
			
		||||
                <b><a style="width:100%;" href="https://youtube.com/channel/<%= video["ucid"] %>"><%= video["author"] %></a></b>
 | 
			
		||||
                <b><a style="width:100%;" href="https://youtube.com/channel/<%= video.author %>"><%= video.author %></a></b>
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>
 | 
			
		||||
                <h5>Shared <%= video["published"].as(Time).to_s("%B %-d, %Y at %r") %></h5>
 | 
			
		||||
                <h5>Shared <%= video.published.to_s("%B %-d, %Y at %r") %></h5>
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user