mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-06-17 15:05:39 +02:00
initial Go port of streamrip
This commit is contained in:
202
internal/urlparse/parse.go
Normal file
202
internal/urlparse/parse.go
Normal file
@@ -0,0 +1,202 @@
|
||||
package urlparse
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type URLKind string
|
||||
|
||||
const (
|
||||
KindGeneric URLKind = "generic"
|
||||
KindDeezerDynamic URLKind = "deezer_dynamic"
|
||||
KindSoundcloud URLKind = "soundcloud"
|
||||
)
|
||||
|
||||
type ParsedURL struct {
|
||||
OriginalURL string
|
||||
Source string
|
||||
MediaType string
|
||||
ID string
|
||||
Kind URLKind
|
||||
}
|
||||
|
||||
var deezerDynamicRe = regexp.MustCompile(`^https?://dzr\.page\.link/`)
|
||||
|
||||
func Parse(raw string) *ParsedURL {
|
||||
if deezerDynamicRe.MatchString(raw) {
|
||||
return &ParsedURL{
|
||||
OriginalURL: raw,
|
||||
Source: "deezer",
|
||||
Kind: KindDeezerDynamic,
|
||||
}
|
||||
}
|
||||
|
||||
u, err := url.Parse(raw)
|
||||
if err != nil || u.Host == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
host := normalizeHost(u.Host)
|
||||
path := strings.Trim(u.EscapedPath(), "/")
|
||||
parts := splitParts(path)
|
||||
|
||||
switch {
|
||||
case isQobuzHost(host):
|
||||
return parseQobuz(raw, parts)
|
||||
case isTidalHost(host):
|
||||
return parseTidal(raw, parts)
|
||||
case isDeezerHost(host):
|
||||
return parseDeezer(raw, parts)
|
||||
case host == "soundcloud.com":
|
||||
return parseSoundcloud(raw, parts)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func parseQobuz(raw string, parts []string) *ParsedURL {
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isLocaleToken(parts[0]) {
|
||||
parts = parts[1:]
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mediaType := parts[0]
|
||||
if !isSupportedMedia(mediaType) {
|
||||
return nil
|
||||
}
|
||||
id := parts[len(parts)-1]
|
||||
if id == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ParsedURL{OriginalURL: raw, Source: "qobuz", MediaType: mediaType, ID: id, Kind: KindGeneric}
|
||||
}
|
||||
|
||||
func parseTidal(raw string, parts []string) *ParsedURL {
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if parts[0] == "browse" {
|
||||
parts = parts[1:]
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mediaType := parts[0]
|
||||
if !isSupportedMedia(mediaType) {
|
||||
return nil
|
||||
}
|
||||
id := parts[1]
|
||||
if id == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ParsedURL{OriginalURL: raw, Source: "tidal", MediaType: mediaType, ID: id, Kind: KindGeneric}
|
||||
}
|
||||
|
||||
func parseDeezer(raw string, parts []string) *ParsedURL {
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isLangToken(parts[0]) {
|
||||
parts = parts[1:]
|
||||
}
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mediaType := parts[0]
|
||||
if !isSupportedMedia(mediaType) {
|
||||
return nil
|
||||
}
|
||||
id := parts[1]
|
||||
if id == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ParsedURL{OriginalURL: raw, Source: "deezer", MediaType: mediaType, ID: id, Kind: KindGeneric}
|
||||
}
|
||||
|
||||
func parseSoundcloud(raw string, parts []string) *ParsedURL {
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mediaType := "track"
|
||||
if len(parts) >= 3 && parts[1] == "sets" {
|
||||
mediaType = "playlist"
|
||||
}
|
||||
|
||||
return &ParsedURL{OriginalURL: raw, Source: "soundcloud", MediaType: mediaType, ID: raw, Kind: KindSoundcloud}
|
||||
}
|
||||
|
||||
func splitParts(path string) []string {
|
||||
if path == "" {
|
||||
return nil
|
||||
}
|
||||
raw := strings.Split(path, "/")
|
||||
parts := make([]string, 0, len(raw))
|
||||
for _, p := range raw {
|
||||
if p != "" {
|
||||
parts = append(parts, p)
|
||||
}
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
func normalizeHost(host string) string {
|
||||
h := strings.ToLower(host)
|
||||
return strings.TrimPrefix(h, "www.")
|
||||
}
|
||||
|
||||
func isQobuzHost(host string) bool {
|
||||
return host == "qobuz.com" || host == "open.qobuz.com" || host == "play.qobuz.com"
|
||||
}
|
||||
|
||||
func isTidalHost(host string) bool {
|
||||
return host == "tidal.com" || host == "open.tidal.com" || host == "listen.tidal.com"
|
||||
}
|
||||
|
||||
func isDeezerHost(host string) bool {
|
||||
return host == "deezer.com"
|
||||
}
|
||||
|
||||
func isSupportedMedia(mediaType string) bool {
|
||||
switch mediaType {
|
||||
case "album", "track", "playlist", "artist", "label":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isLocaleToken(s string) bool {
|
||||
if len(s) != 5 {
|
||||
return false
|
||||
}
|
||||
return s[2] == '-' && isAlpha(s[:2]) && isAlpha(s[3:])
|
||||
}
|
||||
|
||||
func isLangToken(s string) bool {
|
||||
return len(s) == 2 && isAlpha(s)
|
||||
}
|
||||
|
||||
func isAlpha(s string) bool {
|
||||
for _, r := range s {
|
||||
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user