FreeTube icon indicating copy to clipboard operation
FreeTube copied to clipboard

[Feature Request]: Export/save playlists as URLs.txt or CSV file for downloaders

Open sheffieldnikki opened this issue 2 days ago • 0 comments

Guidelines

Problem Description

It would be nice to be able to export a playlist from FreeTube for use in external tools. eg, downloading everything using Tartube (yt-dlp). At the moment there is only Settings->Data->Export playlists as FreeTube's own db format.

Proposed Solution

Possibly not worth adding to FreeTube itself? (rarely used feature) but I wrote a simple Python script that can export a plain text list of URLs, or a CSV file with video title, author, length, etc. Utility scripts like this could be bundled with FreeTube in a separate folder?

$ ./export-playlist.py --help
usage: export-playlist.py [-h] [--csv] [--list LIST] [--db DB]

Export playlists from the FreeTube app

options:
  -h, --help   show this help message and exit
  --csv        output CSV format instead of plain list of URLs
  --list LIST  named playlist
  --db DB      playlists.db file location

$ ./export-playlist.py
playlistName,videos,lastUpdated
"Favorites",10,"21 Feb 2025"
"Electronics",38,"05 Feb 2025"
"Music",268,"24 Feb 2025"
$ ./export-playlist.py --list Electronics
https://youtu.be/byVPAfodTyY
https://youtu.be/T45wk926us4
https://youtu.be/SoDr7vtm_yA
...
$ ./export-playlist.py --list Electronics --csv
videoId,title,author,lengthSeconds,timeAdded
byVPAfodTyY,"How to Install the ESP-IDF Toolchain on Windows","Espressif Systems",290,"31 Dec 2024"
T45wk926us4,"Python NumPy Tutorial for Beginners #1 - Creating Arrays and Dimensional Arrays","Code of the Future",773,"31 Dec 2024"
SoDr7vtm_yA,"Making a 10 cent solder stencil using KiCad, kapton tape, and a laser cutter","elexperiment",256,"31 Dec 2024"
...


#!/usr/bin/env python3
"""Export playlists from the FreeTube app as:
1. Plain text list of YouTube URLs (default), or 
2. CSV file of video details (with --csv)"""
# This work is licensed under CC0 Public Domain
# by Nikki Smith 2025, https://Climbers.net

import argparse
import sys
import os
import json
from datetime import datetime

# Uncomment if you want to strip common phrases from video titles
striptitles = {}
#striptitles = {" (Official Video)", " (Official Music Video)",
#    " (Official HD Video)", " (Official Audio)", " [Official Audio]",
#    " [OFFICIAL VIDEO]", " [Official Music Video]", " [Official Video]",
#    " (Official 4K Video)"}

parser = argparse.ArgumentParser(description='Export playlists from the FreeTube app')
parser.add_argument('--csv', action='store_true',
    help='output CSV format instead of plain list of URLs')
parser.add_argument('--list', help='named playlist')
parser.add_argument('--db', help='playlists.db file location')
args = parser.parse_args()

if args.db:
    fname = args.db
elif sys.platform == "win32":
    fname = os.path.expandvars(r"%APPDATA%\FreeTube\playlists.db")
else:
    fname = os.path.expanduser("~/.config/FreeTube/playlists.db")
fp = open(fname, encoding='utf-8')
listFound = False

# JSON playlists, one per line
if args.list is None:
    print("playlistName,videos,lastUpdated")
for playlist in fp:
    data = json.loads(playlist)
    if args.list is None:
        lastUpdated = datetime.fromtimestamp(data['lastUpdatedAt'] / 1000).strftime("%d %b %Y")
        name = data['playlistName'].replace('"', '""')
        print("\"%s\",%d,\"%s\"" % (name, len(data['videos']), lastUpdated))
    elif args.list.casefold() == data['playlistName'].casefold():
        listFound = True
        if args.csv:
            print("videoId,title,author,lengthSeconds,timeAdded")
        for video in data['videos']:
            if args.csv:
                timeAdded = datetime.fromtimestamp(video['timeAdded'] / 1000).strftime("%d %b %Y")
                title = video['title'].replace('"', '""')
                for x in striptitles:
                    title = title.replace(x, "")
                author = video['author'].replace('"', '""')
                print("%s,\"%s\",\"%s\",%d,\"%s\"" %
                     (video['videoId'],title,author,video['lengthSeconds'],timeAdded))
            else:
                print("https://youtu.be/%s" % video['videoId'])
if args.list is not None and listFound is False:
    raise ValueError('List not found in playlists')
fp.close()

Alternatives Considered

Clicking on every video in a playlist and manually cut'n'pasting all the URLs ;)

Issue Labels

support for external software

Additional Information

No response

sheffieldnikki avatar Feb 25 '25 11:02 sheffieldnikki