Skip to content
This repository was archived by the owner on Mar 16, 2026. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ Lib*
Scripts*
pyvenv.cfg

# Runtime files
share
include
__pycache__
.vscode
.flaskenv
*.db
*.pem
migrations
bin
downloads/*
.cache
.cache
.env*

# Code editors
.idea
.vscode
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ Made with :heart: by <a href="https://github.com/JVT038" target="_blank">JVT038<
- [X] Fix error `Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience. For more help http://xhr.spec.whatwg.org/` in overview
- [X] Make sure the search for downloaded song field works
- [x] Make sure the progress bar works properly in a Docker container, because it doesn't work properly rn
- [x] Use proper queues and threading during download instead of the weird ping-pong system between the client and the server.

### Not finished (I'll probably never finish this lol)

Expand All @@ -283,10 +284,11 @@ Made with :heart: by <a href="https://github.com/JVT038" target="_blank">JVT038<
- [ ] Have a proper versioning system, because it's impossible to keep track of versions rn
- [ ] Cache and store the segments and other video data, so next time of loading a video will be faster
- [ ] Send websocket requests to one specific device / client only, to prevent duplicate websocket requests
- [ ] Use proper queues and threading during download instead of the weird ping-pong system between the client and the server.*
- [ ] Add unit tests for the download, metadata logic, template / database stuff, config detection, automatic migrations
- [ ] Add unit tests for the download, metadata logic, template / database stuff, config detection, automatic migrations *
- [ ] Develop a plugin for yt-dlp to merge the metadata and handle it with postprocessors instead of a custom class
- [ ] Perform bulk actions for multiple items (such as changing the output template or the directory)

* in progress
\* in progress
&#xa0;

## Disclaimer
Expand Down
3 changes: 2 additions & 1 deletion config.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ class Config(object):
VIDEO_EXTENSIONS = ['MP4', 'M4A', 'FLV', 'WEBM', 'OGG', 'MKV', 'AVI']
AUDIO_EXTENSIONS = ['AAC', 'FLAC', 'MP3', 'M4A', 'OPUS', 'VORBIS', 'WAV']
INIT_DB = os.environ.get('INIT_DB', True)
TESTING = False
TESTING = False
DEFAULT_COVER_PATH = os.path.join(BASE_DIR, 'metatube/static/images/empty_cover.png')
7 changes: 7 additions & 0 deletions metatube/DatabaseExceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .Exception import MetaTubeException

class InvalidItemId(MetaTubeException):
pass

class NoDefaultTemplate(MetaTubeException):
pass
6 changes: 6 additions & 0 deletions metatube/Exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class MetaTubeException(Exception):
def __init__(self, message, payload=None):
self.message = message
self.payload = payload
def __str__(self):
return str(self.message)
18 changes: 11 additions & 7 deletions metatube/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@
logger = logging.Logger('default')
logger.addHandler(console)

from metatube.settings import bp as bp_settings
from metatube.overview import bp as bp_overview
from metatube.routes import error

from metatube.init import init as init_db

def create_app(config_class=Config):
Expand All @@ -34,16 +30,24 @@ def create_app(config_class=Config):
FLASK_DEBUG=False,
FLASK_ENV='production'
)
app.register_error_handler(Exception, error)
app.logger.removeHandler(default_handler)
app.logger.addHandler(console)

with app.app_context():
from metatube.settings import bp as bp_settings
from metatube.overview import bp as bp_overview
from metatube.routes import error
from metatube.Exception import MetaTubeException

app.register_blueprint(bp_overview)
app.register_blueprint(bp_settings)
app.register_error_handler(MetaTubeException, error)

console.setLevel(int(app.config["LOG_LEVEL"]))
socket_log = logger if strtobool(str(app.config["SOCKET_LOG"])) == 1 else False
db.init_app(app)
migrate.init_app(app, db, compare_type=True, ping_interval=60)
socketio.init_app(app, json=json, engineio_logger=socket_log, logger=socket_log, async_mode='gevent')
app.register_blueprint(bp_overview)
app.register_blueprint(bp_settings)
if app.config.get('INIT_DB') == True:
init_db(app)
return app
Expand Down
31 changes: 18 additions & 13 deletions metatube/database.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from metatube import db, logger, sockets
from .DatabaseExceptions import *
from sqlalchemy.sql import expression
from dateutil import parser

Expand All @@ -21,7 +22,6 @@ def ffmpeg(self, ffmpeg_path):

@staticmethod
def get_ffmpeg():
# return db.session.get(Config, 1).ffmpeg_directory
return db.session.get(Config, 1).ffmpeg_directory# type: ignore

@staticmethod
Expand Down Expand Up @@ -131,7 +131,10 @@ def delete(self):

@staticmethod
def searchdefault():
return Templates.query.filter_by(default = True).first()
default = Templates.query.filter_by(default = True).first()
if default is None:
raise NoDefaultTemplate("There is no template marked as 'default'!")
return default

def setdefault(self, defaulttemplate = None):
self.default = True
Expand Down Expand Up @@ -166,9 +169,8 @@ class Database(db.Model):
artist = db.Column(db.String(64))
album = db.Column(db.String(64))
date = db.Column(db.DateTime)
length = db.Column(db.Integer)
cover = db.Column(db.String(256))
audio_id = db.Column(db.String(128))
songid = db.Column(db.String(128))
youtube_id = db.Column(db.String(16), unique=True)

@staticmethod
Expand All @@ -188,7 +190,10 @@ def getrecords():

@staticmethod
def fetchitem(input_id):
return Database.query.filter_by(id = input_id).first()
item = Database.query.filter_by(id = input_id).first()
if item is None:
raise InvalidItemId("Invalid item ID")
return item

@staticmethod
def checkfile(filepath_input):
Expand All @@ -199,20 +204,22 @@ def checkyt(youtube_id_input):
return Database.query.filter_by(youtube_id = youtube_id_input).first()

@staticmethod
def checktrackid(release_id_input):
return Database.query.filter_by(audio_id = release_id_input).first()
def songidexists(songid_input) -> bool:
if Database.query.filter_by(songid = songid_input).first() is None:
return False
return True

@staticmethod
def insert(data):
row = Database(
filepath = data["filepath"],
name = data["name"],
artist = '; '.join(data["artist"]),
artist = data["artist"],
album = data["album"],
date = parser.parse(data["date"]),
cover = data["image"],
audio_id = data["track_id"],
youtube_id = data["ytid"]
songid = data["songid"],
youtube_id = data["youtube_id"]
) # type: ignore
db.session.add(row)
db.session.commit()
Expand All @@ -225,14 +232,12 @@ def update(self, data):
self.artist = data["artist"]
self.album = data["album"]
self.date = data["date"]
self.length = data["length"]
self.cover = data["image"]
self.audio_id = data["track_id"]
self.songid = data["songid"]
self.youtube_id = data["youtube_id"]
db.session.commit()
logger.info('Updated item %s', data["name"])
data["date"] = data["date"].strftime('%d-%m-%Y')
sockets.overview({'msg': 'changed_metadata_db', 'data': data})

def updatefilepath(self, filepath):
self.filepath = filepath
Expand Down
34 changes: 18 additions & 16 deletions metatube/deezer.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import deezer
from metatube import sockets
class Deezer():
def socketsearch(data):
client = deezer.Client()
searchresults = client.search(data["title"], artist=data["artist"])
list = []
for result in searchresults:
list.append(result.as_dict())
maxlist = list[0:int(data["max"])]
maxlist.append(data["title"])
sockets.deezersearch(maxlist)
@staticmethod
def socketsearch(data: dict) -> None:
with deezer.Client() as client:
searchresults = client.search(data["title"], artist=data["artist"])
list = []
for result in searchresults:
list.append(result.as_dict())
maxlist = list[0:int(data["max"])]
maxlist.append(data["title"])
sockets.deezersearch(maxlist)

def searchid(id):
client = deezer.Client()
return client.get_track(id).as_dict()
@staticmethod
def searchid(id: int) -> dict:
with deezer.Client() as client:
return client.get_track(id).as_dict()

def sockets_track(id):
client = deezer.Client()
sockets.deezertrack(client.get_track(id).as_dict())
@staticmethod
def sockets_track(id: int) -> None:
with deezer.Client() as client:
sockets.deezertrack(client.get_track(id).as_dict())
4 changes: 3 additions & 1 deletion metatube/genius.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ class Genius():
def __init__(self, client_id):
try:
self.genius = geniusobj(client_id)
except TypeError() as e:
except TypeError as e:
logger.error('Genius API failed: %s', str(e))
raise e from e

def search(self, data):
search = self.genius.search_songs(data["title"], data["max"])
sockets.geniussearch(search)
logger.info('Searched Genius for track \'%s\' ', data["title"])

@staticmethod
def searchsong(data, token):
genius = Genius(token)
genius.search(data)
Expand Down
Loading