added youtube playlist import functionality. fixes issue #2114

Signed-off-by: Gavin Johnson <gavinj1984@gmail.com>
This commit is contained in:
Gavin Johnson 2023-01-28 09:26:16 -08:00
parent 855202e40e
commit 96344f28b4
5 changed files with 86 additions and 72 deletions

View File

@ -33,7 +33,7 @@
"Import": "Import", "Import": "Import",
"Import Invidious data": "Import Invidious JSON data", "Import Invidious data": "Import Invidious JSON data",
"Import YouTube subscriptions": "Import YouTube/OPML subscriptions", "Import YouTube subscriptions": "Import YouTube/OPML subscriptions",
"Import YouTube playlist": "Import YouTube playlist (.csv)", "Import YouTube playlist (.csv)": "Import YouTube playlist (.csv)",
"Import FreeTube subscriptions (.db)": "Import FreeTube subscriptions (.db)", "Import FreeTube subscriptions (.db)": "Import FreeTube subscriptions (.db)",
"Import NewPipe subscriptions (.json)": "Import NewPipe subscriptions (.json)", "Import NewPipe subscriptions (.json)": "Import NewPipe subscriptions (.json)",
"Import NewPipe data (.zip)": "Import NewPipe data (.zip)", "Import NewPipe data (.zip)": "Import NewPipe data (.zip)",

View File

@ -310,6 +310,16 @@ module Invidious::Routes::PreferencesRoute
response: error_template(415, "Invalid subscription file uploaded") response: error_template(415, "Invalid subscription file uploaded")
) )
end end
# Gavin Johnson (thtmnisamnstr), 20230127: Call the Youtube playlist import function
when "import_youtube_pl"
filename = part.filename || ""
success = Invidious::User::Import.from_youtube_pl(user, body, filename, type)
if !success
haltf(env, status_code: 415,
response: error_template(415, "Invalid playlist file uploaded")
)
end
when "import_freetube" when "import_freetube"
Invidious::User::Import.from_freetube(user, body) Invidious::User::Import.from_freetube(user, body)
when "import_newpipe_subscriptions" when "import_newpipe_subscriptions"

View File

@ -30,75 +30,72 @@ struct Invidious::User
return subscriptions return subscriptions
end end
# Parse a youtube CSV playlist file and create the playlist # Gavin Johnson (thtmnisamnstr), 20230127: Parse a youtube CSV playlist file and create the playlist
#NEW - Done
def parse_playlist_export_csv(user : User, csv_content : String) def parse_playlist_export_csv(user : User, csv_content : String)
rows = CSV.new(csv_content, headers: false) rows = CSV.new(csv_content, headers: true)
if rows.size >= 2 row_counter = 0
title = rows[1][4]?.try &.as_s?.try playlist = uninitialized InvidiousPlaylist
descripton = rows[1][5]?.try &.as_s?.try title = uninitialized String
visibility = rows[1][6]?.try &.as_s?.try description = uninitialized String
visibility = uninitialized String
rows.each do |row|
if row_counter == 0
title = row[4]
description = row[5]
visibility = row[6]
if visibility.compare("Public", case_insensitive: true) == 0 if visibility.compare("Public", case_insensitive: true) == 0
privacy = PlaylistPrivacy:Public privacy = PlaylistPrivacy::Public
else else
privacy = PlaylistPrivacy:Private privacy = PlaylistPrivacy::Private
end
if title && privacy && user
playlist = create_playlist(title, privacy, user)
end
if playlist && description
Invidious::Database::Playlists.update_description(playlist.id, description)
end
row_counter += 1
end end
if row_counter > 0 && row_counter < 3
if title && privacy && user row_counter += 1
playlist = create_playlist(title, privacy, user)
end end
if row_counter >= 3
if playlist
video_id = row[0]
row_counter += 1
next if !video_id
if playlist && descripton begin
Invidious::Database::Playlists.update_description(playlist.id, description) video = get_video(video_id)
rescue ex
next
end
playlist_video = PlaylistVideo.new({
title: video.title,
id: video.id,
author: video.author,
ucid: video.ucid,
length_seconds: video.length_seconds,
published: video.published,
plid: playlist.id,
live_now: video.live_now,
index: Random::Secure.rand(0_i64..Int64::MAX),
})
Invidious::Database::PlaylistVideos.insert(playlist_video)
Invidious::Database::Playlists.update_video_added(playlist.id, playlist_video.index)
end
end end
end end
return playlist return playlist
end end
# Parse a youtube CSV playlist file and add videos from it to a playlist
#NEW - done
def parse_playlist_videos_export_csv(playlist : Playlist, csv_content : String)
rows = CSV.new(csv_content, headers: false)
if rows.size >= 5
offset = env.params.query["index"]?.try &.to_i? || 0
row_counter = 0
rows.each do |row|
if row_counter >= 4
video_id = row[0]?.try &.as_s?.try
end
row_counter += 1
next if !video_id
begin
video = get_video(video_id)
rescue ex
next
end
playlist_video = PlaylistVideo.new({
title: video.title,
id: video.id,
author: video.author,
ucid: video.ucid,
length_seconds: video.length_seconds,
published: video.published,
plid: playlist.id,
live_now: video.live_now,
index: Random::Secure.rand(0_i64..Int64::MAX),
})
Invidious::Database::PlaylistVideos.insert(playlist_video)
Invidious::Database::Playlists.update_video_added(playlist.id, playlist_video.index)
end
videos = get_playlist_videos(playlist, offset: offset)
end
return videos
end
# ------------------- # -------------------
# Invidious # Invidious
# ------------------- # -------------------
@ -218,20 +215,20 @@ struct Invidious::User
return true return true
end end
# Import playlist from Youtube # Gavin Johnson (thtmnisamnstr), 20230127: Import playlist from Youtube export. Returns success status.
# Returns success status
#NEW
def from_youtube_pl(user : User, body : String, filename : String, type : String) : Bool def from_youtube_pl(user : User, body : String, filename : String, type : String) : Bool
extension = filename.split(".").last extension = filename.split(".").last
if extension == "csv" || type == "text/csv" if extension == "csv" || type == "text/csv"
playlist = parse_playlist_export_csv(user, body) playlist = parse_playlist_export_csv(user, body)
playlist = parse_playlist_videos_export_csv(playlist, body) if playlist
return true
else
return false
end
else else
return false return false
end end
return true
end end
# ------------------- # -------------------

View File

@ -5,14 +5,21 @@
<%= rendered "components/feed_menu" %> <%= rendered "components/feed_menu" %>
<div class="pure-g h-box"> <div class="pure-g h-box">
<div class="pure-u-2-3"> <div class="pure-u-1-3">
<h3><%= translate(locale, "user_created_playlists", %(<span id="count">#{items_created.size}</span>)) %></h3> <h3><%= translate(locale, "user_created_playlists", %(<span id="count">#{items_created.size}</span>)) %></h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:right"> <div class="pure-u-1-3">
<h3> <h3 style="text-align:center">
<a href="/create_playlist?referer=<%= URI.encode_www_form(referer) %>"><%= translate(locale, "Create playlist") %></a> <a href="/create_playlist?referer=<%= URI.encode_www_form(referer) %>"><%= translate(locale, "Create playlist") %></a>
</h3> </h3>
</div> </div>
<div class="pure-u-1-3">
<h3 style="text-align:right">
<a href="/data_control?referer=<%= URI.encode_www_form(referer) %>">
<%= translate(locale, "Import/export") %>
</a>
</h3>
</div>
</div> </div>
<div class="pure-g"> <div class="pure-g">

View File

@ -8,7 +8,7 @@
<legend><%= translate(locale, "Import") %></legend> <legend><%= translate(locale, "Import") %></legend>
<div class="pure-control-group"> <div class="pure-control-group">
<label for="import_youtube"><%= translate(locale, "Import Invidious data") %></label> <label for="import_invidious"><%= translate(locale, "Import Invidious data") %></label>
<input type="file" id="import_invidious" name="import_invidious"> <input type="file" id="import_invidious" name="import_invidious">
</div> </div>
@ -22,7 +22,7 @@
</div> </div>
<div class="pure-control-group"> <div class="pure-control-group">
<label for="import_youtube_pl"><%= translate(locale, "Import YouTube playlists") %></label> <label for="import_youtube_pl"><%= translate(locale, "Import YouTube playlist (.csv)") %></label>
<input type="file" id="import_youtube_pl" name="import_youtube_pl"> <input type="file" id="import_youtube_pl" name="import_youtube_pl">
</div> </div>