Skip to content

Commit

Permalink
Merge pull request #76 from JVT038/Develop
Browse files Browse the repository at this point in the history
Added Genius support with some small bug fixes and such
  • Loading branch information
JVT038 authored Feb 19, 2023
2 parents 6672f8a + 35c160d commit b4ec4e5
Show file tree
Hide file tree
Showing 23 changed files with 1,175 additions and 201 deletions.
47 changes: 35 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,28 @@

<h2 align="center">Status</h2>

<h4 align="center">
🚧 MetaTube 🚀 Under construction... 🚧 <br/>
<h4 align="center">
:heavy_check_mark: MetaTube 🚀 Finished! :heavy_check_mark: <br/>
</h4>

<hr>

<p align="center">
<a href="#dart-about">About</a> &#xa0; | &#xa0;
<a href="#dart-about">About</a> &#xa0; | &#xa0;
<a href="#sparkles-features">Features</a> &#xa0; | &#xa0;
<a href="#rocket-technologies">Technologies</a> &#xa0; | &#xa0;
<a href="#white_check_mark-requirements">Requirements</a> &#xa0; | &#xa0;
<a href="#checkered_flag-starting">Starting</a> &#xa0; | &#xa0;
<a href="#memo-license">License</a> &#xa0; | &#xa0;
<a href="#disclaimer">Disclaimer</a> &#xa0; | &#xa0;
<a href="#disclaimer">Disclaimer</a> &#xa0; | &#xa0;
<a href="https://github.com/JVT038" target="_blank">Author</a>
</p>

<br>

## :dart: About ##

MetaTube downloads video from YouTube and can add metadata from a specified metadata provider on the downloaded file.
MetaTube downloads video from YouTube and can add metadata from a specified metadata provider on the downloaded file.
Normal view | Dark mode|
--- | ---
![startpage](https://user-images.githubusercontent.com/47184046/147980156-e3ee71e4-a4cd-4fee-808b-c4b3c9530e9f.png) | ![darkstartpage](https://user-images.githubusercontent.com/47184046/147980017-bd3bc8bf-2589-4ee5-8d9c-1785ba906982.png)
Expand Down Expand Up @@ -103,8 +103,11 @@ For a complete list, visit the [Dependencies overview](https://github.com/JVT038
Before starting :checkered_flag:, you need to have [Git](https://git-scm.com) and [Python 3.8 or higher](https://python.org/downloads) installed.

## :checkered_flag: Starting ##

### :whale: Using Docker ###

CLI docker:

```docker
docker run \
-d \
Expand All @@ -117,7 +120,9 @@ docker run \
-v /metatube:/database:rw \
jvt038/metatube:latest
```

Docker-compose:

```
version: '3.3'
services:
Expand All @@ -134,8 +139,11 @@ services:
- '/downloads:/downloads:rw'
- '/metatube:/database:rw'
```

You need to set the variable `DATABASE_URL` to a custom mount point (in these examples `/database`), because otherwise your database file will reset everytime the Docker container updates.

### :hammer_and_wrench: Manually build and start server ###

```bash
# Clone this project
$ git clone https://github.com/JVT038/metatube
Expand Down Expand Up @@ -173,6 +181,7 @@ $ python metatube.py

# The server will initialize in the <http://localhost:5000>
```

You can set the following environment variables:
Name | Description | Default value
---|---|---
Expand All @@ -186,33 +195,45 @@ LOG | Whether to keep logs or not | False
SOCKET_LOG | Whether to log in- and outcoming websocket connections; warning: your console can be spammed with connections | False
LOG_LEVEL | Numeric value from which MetaTube will keep logs. Info [here](https://docs.python.org/3/howto/logging.html#logging-levels) | 10
URL_SUBPATH | Set the URL subpath, if you want to run MetaTube on a subpath. Example: `/metatube` will run the server on `host:port/metatube` | /

```bash
# On Windows 10, you can set an environment variable like this:
$ set ENVIRONMENT_VARIABLE = Value

# On Linux and MacOS, you can set an environment variable like this:
$ export ENVIRONMENT_VARIABLE = Value
```

Additionally you can create a file called `.flaskenv` and set the environment variables in there.
An example is provided in [example.flaskenv](example.flaskenv). You can use that template and rename the file to `.flaskenv`.

## Fix the artist values

So I recently discovered I made a mistake in the process of adding artists to files. <br/>
Some songs have tags multiple artists, and I noticed these tags were misinterpreted by my audio player. <br/>
Basically, the `TPE1` tag contained was like this: `['artist 1; artist 2']`, while it should've been `['artist 1', 'artist 2']`. <br/>
Thanks to [#310](https://github.com/quodlibet/mutagen/issues/310) I discovered this, corrected it in `metadata.py` and wrote a small script in [fixartists.py](fixartists.py) to fix the existing audio files that had the tags in the wrong way. <br/>
Put all the wrong audio files in one directory, run the file and enter the path to the directory containing the incorrect tags, and it should be fixed. <br/>
My apologies for this (annoying) bug.

## :memo: License ##

This project is under license from GNUv3. For more details, see the [LICENSE](LICENSE) file.<br/>
I am not responsible for any legal consequences the user may or may not face by using this project.


Made with :heart: by <a href="https://github.com/JVT038" target="_blank">JVT038</a>

## To-Do

### Finished

- [X] Add support for the use of proxies to download YouTube videos
- [X] Add Docker support
- [X] Add Docker support for ARM64/v8 devices (such as Raspberry Pi 4)
- [X] Add Github action / workflow thing, to automatically create Docker image upon a new commit
- [X] Add support for Spotify
- [X] Add support for Deezer
- [X] Add support for Spotify as a metadata provider
- [X] Add support for Deezer as a metadata provider
- [X] Add support for Genius as a metadata provider
- [X] Add support for subpath (such as `localhost:5000/metatube`)
- [X] Add a nice progress bar
- [X] Add a function to allow users to download the song onto their device
Expand All @@ -237,8 +258,8 @@ Made with :heart: by <a href="https://github.com/JVT038" target="_blank">JVT038<
- [X] Dark mode support
- [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
### Not finished (I probably will never finish this)

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

- [ ] Add it to the PyPi library
- [ ] Add support for sites other than YouTube
Expand All @@ -247,8 +268,9 @@ Made with :heart: by <a href="https://github.com/JVT038" target="_blank">JVT038<
- [ ] Add support for H.265 / HEVC
- [ ] Add authentication system with an optional reverse proxy
- [ ] Add support for TheAudioDB
- [ ] Add support for YouTube Music
- [ ] Add support for YouTube Music
- [ ] Add support for Last.fm!
- [ ] Add support for embedded lyrics (if possible)
- [ ] Add translations
- [ ] Add in-built file explorer, making manual paths optional
- [ ] Add some nice animations
Expand All @@ -263,13 +285,14 @@ Made with :heart: by <a href="https://github.com/JVT038" target="_blank">JVT038<
- [ ] 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
- [ ] Make sure the progress bar works properly in a Docker container, because it doesn't work properly rn.
- [ ] Use proper queues and threading during download instead of the weird ping-pong system between the client and the server.
&#xa0;

## Disclaimer

I made this project to educate myself about Python, and to learn how metadata works in combination with files.
Additionally, I want to emphasize I do NOT encourage any pirating, or any other illegal activities.
This project's purpose isn't to illegally download content from YouTube; its purpose is to educate and enlighten myself (and others viewing the source code) about Python, how Python interacts with metadata in files, and metadata works, and how yt-dlp works.
I am not responsible if the user downloads illegal content, or faces any (legal) consequences.


<a href="#top">Back to top</a>
5 changes: 4 additions & 1 deletion config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ class Config(object):
PORT = os.environ.get('PORT', 5000)
FFMPEG = os.environ.get('FFMPEG', "")
DOWNLOADS = os.environ.get('DOWNLOADS', os.path.join(basedir, 'downloads'))
URL_SUBPATH = os.environ.get('URL_SUBPATH', '/')
URL_SUBPATH = os.environ.get('URL_SUBPATH', '/')
META_EXTENSIONS = ['MP3', 'OPUS', 'FLAC', 'OGG', 'MP4', 'M4A', 'WAV']
VIDEO_EXTENSIONS = ['MP4', 'M4A', 'FLV', 'WEBM', 'OGG', 'MKV', 'AVI']
AUDIO_EXTENSIONS = ['AAC', 'FLAC', 'MP3', 'M4A', 'OPUS', 'VORBIS', 'WAV']
40 changes: 40 additions & 0 deletions fixartists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#################################
#####How to use this script:#####
#1. python fixartists.py#########
#2. Enter directory in CLI#######
#3. Let it run###################
#################################
from mutagen.easyid3 import EasyID3
from mutagen.flac import FLAC
from mutagen.oggopus import OggOpus
from mutagen.oggvorbis import OggVorbis
import os
directory = input('Enter relative or absolute path to audio files: ')
files = os.listdir(directory)
for file in files:
extensions = ['MP3', 'OPUS', 'FLAC', 'OGG']
filepath = os.path.join(directory, file)
extension = filepath.split('.')[len(filepath.split('.')) - 1].upper()
if os.path.isfile(filepath):
if extension.upper() in extensions:
if extension == 'MP3':
audio = EasyID3(filepath)
elif extension == 'OPUS':
audio = OggOpus(filepath)
elif extension == 'FLAC':
audio = FLAC(filepath)
elif extension == 'OGG':
audio = OggVorbis(filepath)
if 'artist' in audio:
old_artists = audio["artist"]
if len(old_artists) == 1 and '; ' in old_artists[0]:
new_artists = old_artists[0].split('; ')
audio["artist"] = new_artists
audio.save()
print(f'Fixed artists of {file}')
else:
print(f'{file} already has the correct artist format, skipping...')
else:
print(f'{file} contains no TPE1 tag, skipping')
else:
print(f'{file} has an unsupported extension, skipping')
18 changes: 17 additions & 1 deletion metatube/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Config(db.Model):
hardware_transcoding = db.Column(db.String(16), default="None")
metadata_sources = db.Column(db.String(128), default='deezer')
spotify_api = db.Column(db.String(128))
genius_api = db.Column(db.String(128))
auth = db.Column(db.Boolean, server_default=expression.false())
auth_username = db.Column(db.String(128))
auth_password = db.Column(db.String(128))
Expand All @@ -31,9 +32,15 @@ def set_amount(self, amount):

def set_spotify(self, spotify):
self.spotify_api = spotify
print(spotify)
db.session.commit()
logger.info('Changed the Spotify API settings')

def set_genius(self, genius):
self.genius_api = genius
db.session.commit()
logger.info('Changed the Genius API settings')

def set_metadata(self, metadata_sources):
self.metadata_sources = metadata_sources
db.session.commit()
Expand All @@ -50,6 +57,9 @@ def get_metadata_sources():
def get_spotify():
return Config.query.get(1).spotify_api

def get_genius():
return Config.query.get(1).genius_api

def get_max():
return Config.query.get(1).amount

Expand Down Expand Up @@ -177,7 +187,7 @@ def insert(data):
row = Database(
filepath = data["filepath"],
name = data["name"],
artist = data["artist"],
artist = '; '.join(data["artist"]),
album = data["album"],
date = parser.parse(data["date"]),
cover = data["image"],
Expand All @@ -203,6 +213,12 @@ def update(self, data):
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
db.session.commit()
logger.info('Updated filepath of item %s to %s', self.name, filepath)
sockets.overview({'msg': 'updated_filepath', 'filepath': filepath, 'item': self.id})

def delete(self):
db.session.delete(self)
Expand Down
26 changes: 26 additions & 0 deletions metatube/genius.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from lyricsgenius import Genius as geniusobj
from metatube import logger, sockets
class Genius():
def __init__(self, client_id):
try:
self.genius = geniusobj(client_id)
except TypeError() as e:
logger.error('Genius API failed: %s', str(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"])

def searchsong(data, token):
genius = Genius(token)
genius.search(data)

def fetchsong(self, id):
return self.genius.song(id)

def fetchlyrics(self, url):
return self.genius.lyrics(url)

def fetchalbum(self, id):
sockets.foundgeniusalbum(self.genius.album_tracks(id))
Loading

0 comments on commit b4ec4e5

Please sign in to comment.