Compare commits
10 Commits
dea3f1bd67
...
68cc740251
Author | SHA1 | Date |
---|---|---|
NaiJi ✨ | 68cc740251 | |
NaiJi ✨ | 87117f2e9e | |
NaiJi ✨ | a1cc611eff | |
NaiJi ✨ | 963fd4bc1a | |
NaiJi ✨ | 578e3709f8 | |
NaiJi ✨ | ebd0e6836d | |
NaiJi ✨ | f9beb23d44 | |
NaiJi ✨ | d8ddc8001a | |
NaiJi ✨ | 23dcc4b74a | |
NaiJi ✨ | f88268caf7 |
|
@ -1,8 +1,8 @@
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
import 'package:funkblubber/funkblubber.dart' as funkblubber;
|
import 'package:funkblubber/funkwhale.dart' as funkblubber;
|
||||||
import 'package:funkblubber/console.dart' as console;
|
import 'package:funkblubber/console.dart' as console;
|
||||||
import 'package:funkblubber/parsing/parser.dart' as parser;
|
import 'package:funkblubber/parser.dart' as parser;
|
||||||
|
|
||||||
void main(final List<String> arguments) async {
|
void main(final List<String> arguments) async {
|
||||||
final result = parser.extract(arguments);
|
final result = parser.extract(arguments);
|
||||||
|
@ -11,5 +11,12 @@ void main(final List<String> arguments) async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await funkblubber.download(object: result.object!);
|
final bool success = await funkblubber.download(
|
||||||
|
object: result.object!,
|
||||||
|
path: result.localPath ?? '.',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
console.error("couldn't execute request successfully.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
import 'dart:core';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:funkblubber/funkentity.dart';
|
|
||||||
import 'package:funkblubber/console.dart' as console;
|
|
||||||
|
|
||||||
Future<void> download({
|
|
||||||
required final FunkObject object,
|
|
||||||
final String path = '.',
|
|
||||||
}) async {
|
|
||||||
switch (object.kind) {
|
|
||||||
case FunkEntity.album:
|
|
||||||
await _downloadAlbum(object, path);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case FunkEntity.artist:
|
|
||||||
await _downloadArtist(object, path);
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _downloadArtist(
|
|
||||||
final FunkObject object,
|
|
||||||
final String path,
|
|
||||||
) async {
|
|
||||||
Response response = await Dio().get(
|
|
||||||
'https://${object.domain}/api/v1/artists/${object.id}/?'
|
|
||||||
'refresh=false',
|
|
||||||
);
|
|
||||||
|
|
||||||
final String pathAppend = response.data['name'];
|
|
||||||
await Directory('$path/$pathAppend').create();
|
|
||||||
|
|
||||||
response = await Dio().get(
|
|
||||||
'https://${object.domain}/api/v1/albums/?'
|
|
||||||
'artist=${object.id}&ordering=creation_date&'
|
|
||||||
'page=1&page_size=16&scope=all',
|
|
||||||
);
|
|
||||||
|
|
||||||
for (final albumResponse in response.data['results']) {
|
|
||||||
_downloadAlbum(
|
|
||||||
FunkObject(
|
|
||||||
domain: object.domain,
|
|
||||||
id: albumResponse['id'].toString(),
|
|
||||||
kind: FunkEntity.album,
|
|
||||||
),
|
|
||||||
'$path/$pathAppend',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _downloadAlbum(
|
|
||||||
final FunkObject object,
|
|
||||||
final String path,
|
|
||||||
) async {
|
|
||||||
final response = await Dio().get(
|
|
||||||
'https://${object.domain}/api/v1/tracks/?'
|
|
||||||
'album=${object.id}&ordering=creation_date&'
|
|
||||||
'page=1&page_size=16&scope=all',
|
|
||||||
);
|
|
||||||
|
|
||||||
final String pathAppend = response.data['results'][0]['album']['title'];
|
|
||||||
await Directory('$path/$pathAppend').create();
|
|
||||||
|
|
||||||
for (final songResponse in response.data['results']) {
|
|
||||||
final String songTitle = songResponse['title'];
|
|
||||||
final String ext = songResponse['uploads'][0]['extension'];
|
|
||||||
_downloadSongObject(
|
|
||||||
FunkObject(
|
|
||||||
domain: object.domain,
|
|
||||||
id: songResponse['listen_url'],
|
|
||||||
kind: FunkEntity.song,
|
|
||||||
),
|
|
||||||
'$path/$pathAppend/$songTitle.$ext',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _downloadSongObject(
|
|
||||||
final FunkObject object,
|
|
||||||
final String path,
|
|
||||||
) async {
|
|
||||||
final Response response = await Dio().get(
|
|
||||||
'https://${object.domain}${object.id}',
|
|
||||||
options: Options(
|
|
||||||
responseType: ResponseType.bytes,
|
|
||||||
followRedirects: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
final File file = File(path);
|
|
||||||
final accessFile = file.openSync(mode: FileMode.write);
|
|
||||||
accessFile.writeFromSync(response.data);
|
|
||||||
await accessFile.close();
|
|
||||||
}
|
|
|
@ -1,7 +1,5 @@
|
||||||
enum FunkEntity {
|
enum FunkEntity {
|
||||||
error,
|
track,
|
||||||
|
|
||||||
song,
|
|
||||||
album,
|
album,
|
||||||
artist,
|
artist,
|
||||||
profile,
|
profile,
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
import 'dart:core';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:funkblubber/funkentity.dart';
|
||||||
|
import 'package:funkblubber/console.dart' as console;
|
||||||
|
import 'package:funkblubber/string_utils.dart' as utils;
|
||||||
|
|
||||||
|
Future<bool> download({
|
||||||
|
required final FunkObject object,
|
||||||
|
required final String path,
|
||||||
|
}) async {
|
||||||
|
bool success = false;
|
||||||
|
console.info('Downloading');
|
||||||
|
switch (object.kind) {
|
||||||
|
case FunkEntity.album:
|
||||||
|
success = await _downloadAlbum(object, path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FunkEntity.artist:
|
||||||
|
success = await _downloadArtist(object, path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FunkEntity.track:
|
||||||
|
success = await _downloadTrack(object, path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.info(' nothing...');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _downloadArtist(
|
||||||
|
final FunkObject object,
|
||||||
|
final String path,
|
||||||
|
) async {
|
||||||
|
Response response = await Dio().get(
|
||||||
|
'https://${object.domain}/api/v1/artists/${object.id}/?'
|
||||||
|
'refresh=false',
|
||||||
|
);
|
||||||
|
|
||||||
|
final String pathAppend = utils.sanitizePath(response.data['name']);
|
||||||
|
bool folderCreated = true;
|
||||||
|
try {
|
||||||
|
await Directory('$path/$pathAppend').create();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.toString());
|
||||||
|
folderCreated = false;
|
||||||
|
}
|
||||||
|
if (!folderCreated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
console.info('Artist: $pathAppend');
|
||||||
|
|
||||||
|
response = await Dio().get(
|
||||||
|
'https://${object.domain}/api/v1/albums/?'
|
||||||
|
'artist=${object.id}&ordering=creation_date&'
|
||||||
|
'page=1&page_size=16&scope=all',
|
||||||
|
);
|
||||||
|
|
||||||
|
final List<Future<bool>> results = [];
|
||||||
|
for (final albumResponse in response.data['results']) {
|
||||||
|
final result = _downloadAlbum(
|
||||||
|
FunkObject(
|
||||||
|
domain: object.domain,
|
||||||
|
id: albumResponse['id'].toString(),
|
||||||
|
kind: FunkEntity.album,
|
||||||
|
),
|
||||||
|
'$path/$pathAppend',
|
||||||
|
);
|
||||||
|
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<bool> successes = await Future.wait(results);
|
||||||
|
return successes.every((final success) => success);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _downloadAlbum(
|
||||||
|
final FunkObject object,
|
||||||
|
final String path,
|
||||||
|
) async {
|
||||||
|
final response = await Dio().get(
|
||||||
|
'https://${object.domain}/api/v1/tracks/?'
|
||||||
|
'album=${object.id}&ordering=creation_date&'
|
||||||
|
'page=1&page_size=16&scope=all',
|
||||||
|
);
|
||||||
|
|
||||||
|
final String pathAppend = utils.sanitizePath(
|
||||||
|
response.data['results'][0]['album']['title'],
|
||||||
|
);
|
||||||
|
bool folderCreated = true;
|
||||||
|
try {
|
||||||
|
await Directory('$path/$pathAppend').create();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.toString());
|
||||||
|
folderCreated = false;
|
||||||
|
}
|
||||||
|
if (!folderCreated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
console.info('Album: $pathAppend');
|
||||||
|
|
||||||
|
final List<Future<bool>> results = [];
|
||||||
|
for (final songResponse in response.data['results']) {
|
||||||
|
final String songTitle = utils.sanitizePath(songResponse['title']);
|
||||||
|
final String ext = songResponse['uploads'][0]['extension'];
|
||||||
|
final result = _downloadTrackObject(
|
||||||
|
FunkObject(
|
||||||
|
domain: object.domain,
|
||||||
|
id: songResponse['listen_url'],
|
||||||
|
kind: FunkEntity.track,
|
||||||
|
),
|
||||||
|
'$path/$pathAppend/$songTitle.$ext',
|
||||||
|
);
|
||||||
|
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<bool> successes = await Future.wait(results);
|
||||||
|
return successes.every((final success) => success);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _downloadTrack(
|
||||||
|
final FunkObject object,
|
||||||
|
final String path,
|
||||||
|
) async {
|
||||||
|
final response = await Dio().get(
|
||||||
|
'https://${object.domain}/api/v1/tracks/${object.id}/?refresh=false',
|
||||||
|
);
|
||||||
|
|
||||||
|
final String songTitle = utils.sanitizePath(response.data['title']);
|
||||||
|
final String ext = response.data['uploads'][0]['extension'];
|
||||||
|
return _downloadTrackObject(
|
||||||
|
FunkObject(
|
||||||
|
domain: object.domain,
|
||||||
|
id: response.data['uploads'][0]['listen_url'],
|
||||||
|
kind: FunkEntity.track,
|
||||||
|
),
|
||||||
|
'$path/$songTitle.$ext',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _downloadTrackObject(
|
||||||
|
final FunkObject object,
|
||||||
|
final String path,
|
||||||
|
) async {
|
||||||
|
bool success = true;
|
||||||
|
try {
|
||||||
|
final Response response = await Dio().get(
|
||||||
|
'https://${object.domain}${object.id}',
|
||||||
|
options: Options(
|
||||||
|
responseType: ResponseType.bytes,
|
||||||
|
followRedirects: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.info('- $path');
|
||||||
|
final File file = File(path);
|
||||||
|
final accessFile = file.openSync(mode: FileMode.write);
|
||||||
|
accessFile.writeFromSync(response.data);
|
||||||
|
await accessFile.close();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.toString());
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
314
lib/parser.dart
314
lib/parser.dart
|
@ -1,11 +1,46 @@
|
||||||
import 'package:funkblubber/funkentity.dart';
|
import 'package:funkblubber/funkentity.dart';
|
||||||
import 'package:funkblubber/console.dart' as console;
|
import 'package:funkblubber/console.dart' as console;
|
||||||
|
import 'package:funkblubber/string_utils.dart' as utils;
|
||||||
|
|
||||||
FunkObject extract(final List<String> args) {
|
enum Action {
|
||||||
FunkObject result = FunkObject(
|
download,
|
||||||
kind: FunkEntity.error,
|
upload,
|
||||||
id: '',
|
}
|
||||||
domain: '',
|
|
||||||
|
enum ParsingStage {
|
||||||
|
nothing,
|
||||||
|
album,
|
||||||
|
artist,
|
||||||
|
track,
|
||||||
|
path,
|
||||||
|
upload,
|
||||||
|
domain,
|
||||||
|
}
|
||||||
|
|
||||||
|
class StageResult {
|
||||||
|
StageResult({required this.result, required this.stage});
|
||||||
|
final ParseResult result;
|
||||||
|
final ParsingStage stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParseResult {
|
||||||
|
ParseResult({
|
||||||
|
required this.success,
|
||||||
|
required this.action,
|
||||||
|
this.object,
|
||||||
|
this.localPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
final FunkObject? object;
|
||||||
|
final String? localPath;
|
||||||
|
final Action action;
|
||||||
|
final bool success;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseResult extract(final List<String> args) {
|
||||||
|
ParseResult result = ParseResult(
|
||||||
|
success: false,
|
||||||
|
action: Action.download,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (args.isEmpty) {
|
if (args.isEmpty) {
|
||||||
|
@ -13,26 +48,227 @@ FunkObject extract(final List<String> args) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParsingStage currentStage = ParsingStage.nothing;
|
||||||
|
|
||||||
|
for (final String arg in args) {
|
||||||
|
switch (currentStage) {
|
||||||
|
case ParsingStage.nothing:
|
||||||
|
final stageResult = _onNothingStage(arg, result, currentStage);
|
||||||
|
currentStage = stageResult.stage;
|
||||||
|
result = stageResult.result;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParsingStage.album:
|
||||||
|
final stageResult = _onEntityStage(
|
||||||
|
arg,
|
||||||
|
result,
|
||||||
|
currentStage,
|
||||||
|
FunkEntity.album,
|
||||||
|
);
|
||||||
|
currentStage = stageResult.stage;
|
||||||
|
result = stageResult.result;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParsingStage.artist:
|
||||||
|
final stageResult = _onEntityStage(
|
||||||
|
arg,
|
||||||
|
result,
|
||||||
|
currentStage,
|
||||||
|
FunkEntity.artist,
|
||||||
|
);
|
||||||
|
currentStage = stageResult.stage;
|
||||||
|
result = stageResult.result;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParsingStage.track:
|
||||||
|
final stageResult = _onEntityStage(
|
||||||
|
arg,
|
||||||
|
result,
|
||||||
|
currentStage,
|
||||||
|
FunkEntity.track,
|
||||||
|
);
|
||||||
|
currentStage = stageResult.stage;
|
||||||
|
result = stageResult.result;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParsingStage.domain:
|
||||||
|
final stageResult = _onDomainStage(arg, result, currentStage);
|
||||||
|
currentStage = stageResult.stage;
|
||||||
|
result = stageResult.result;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ParsingStage.path:
|
||||||
|
final stageResult = _onPathStage(arg, result, currentStage);
|
||||||
|
currentStage = stageResult.stage;
|
||||||
|
result = stageResult.result;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.error('not implemented yet');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onHelpStage() {
|
||||||
|
final String help = '''
|
||||||
|
usage: funkblubber [[-OPTIONS|URL]...]
|
||||||
|
|
||||||
|
A simple CLI for interaction with your Funkwhale account.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
[-a|--album ID] Provide with album ID to download it.
|
||||||
|
|
||||||
|
[-A|--artist ID] Provide with artist ID to download their albums.
|
||||||
|
|
||||||
|
[-t|--track ID] Provide with track ID to download a single track entity.
|
||||||
|
|
||||||
|
[-p|--path PATH] Provide to explicitly define download directory.
|
||||||
|
Assumed '.'.
|
||||||
|
|
||||||
|
[-d|--domain DOMAIN] Provide to explicitly define the host where your
|
||||||
|
Funkwhale instance is. Required with -a, -A, and -u.
|
||||||
|
Is not required if you download by a full URL.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
funkblubber https://my.funkwhale/library/tracks/10244/
|
||||||
|
Download album by URL to current folder.
|
||||||
|
|
||||||
|
funkblubber https://my.funkwhale/library/tracks/10244/ -p music
|
||||||
|
Download album by URL to local ./music folder.
|
||||||
|
|
||||||
|
funkblubber -A 152 -d my.funkwhale -p media/music/
|
||||||
|
Download everything by artist with ID 152 from a funkwhale instance
|
||||||
|
with my.funkwhale domain and save it to local ./media/music folder.
|
||||||
|
|
||||||
|
funkblubber --album 1423 -d my.funkwhale
|
||||||
|
Download album with ID 1423 from a funkwhale instance
|
||||||
|
with my.funkwhale domain and save it to current folder.
|
||||||
|
|
||||||
|
''';
|
||||||
|
|
||||||
|
console.info(help);
|
||||||
|
}
|
||||||
|
|
||||||
|
StageResult _onEntityStage(
|
||||||
|
final String arg,
|
||||||
|
final ParseResult previousResult,
|
||||||
|
final ParsingStage previousStage,
|
||||||
|
final FunkEntity kind,
|
||||||
|
) {
|
||||||
|
ParsingStage currentStage = previousStage;
|
||||||
|
ParseResult result = previousResult;
|
||||||
|
|
||||||
|
if (int.tryParse(arg) != null) {
|
||||||
|
currentStage = ParsingStage.nothing;
|
||||||
|
result = ParseResult(
|
||||||
|
action: previousResult.action,
|
||||||
|
success: true,
|
||||||
|
object: FunkObject(
|
||||||
|
domain: previousResult.object?.domain ?? '',
|
||||||
|
id: arg,
|
||||||
|
kind: kind,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return StageResult(result: result, stage: currentStage);
|
||||||
|
}
|
||||||
|
|
||||||
|
StageResult _onDomainStage(
|
||||||
|
final String arg,
|
||||||
|
final ParseResult previousResult,
|
||||||
|
final ParsingStage previousStage,
|
||||||
|
) {
|
||||||
|
ParsingStage currentStage = previousStage;
|
||||||
|
ParseResult result = previousResult;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Uri uri = Uri.parse(args[0]);
|
final Uri uri = Uri.parse(arg);
|
||||||
|
currentStage = ParsingStage.nothing;
|
||||||
|
result = ParseResult(
|
||||||
|
action: previousResult.action,
|
||||||
|
success: true,
|
||||||
|
object: FunkObject(
|
||||||
|
domain: uri.toString(),
|
||||||
|
id: previousResult.object?.id ?? '',
|
||||||
|
kind: previousResult.object?.kind ?? FunkEntity.album,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return StageResult(result: result, stage: currentStage);
|
||||||
|
}
|
||||||
|
|
||||||
|
StageResult _onPathStage(
|
||||||
|
final String arg,
|
||||||
|
final ParseResult previousResult,
|
||||||
|
final ParsingStage previousStage,
|
||||||
|
) {
|
||||||
|
ParsingStage currentStage = previousStage;
|
||||||
|
ParseResult result = previousResult;
|
||||||
|
|
||||||
|
currentStage = ParsingStage.nothing;
|
||||||
|
result = ParseResult(
|
||||||
|
localPath: utils.cutTrailingDash(arg),
|
||||||
|
action: previousResult.action,
|
||||||
|
success: true,
|
||||||
|
object: previousResult.object,
|
||||||
|
);
|
||||||
|
|
||||||
|
return StageResult(result: result, stage: currentStage);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseResult _makeParseResultFromEntityInfo(
|
||||||
|
final FunkEntity kind,
|
||||||
|
final String host,
|
||||||
|
final String id,
|
||||||
|
) =>
|
||||||
|
ParseResult(
|
||||||
|
action: Action.download,
|
||||||
|
success: true,
|
||||||
|
object: FunkObject(
|
||||||
|
kind: kind,
|
||||||
|
id: id,
|
||||||
|
domain: host,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
ParseResult _parseUrl(final String url, final ParseResult previousResult) {
|
||||||
|
ParseResult result = previousResult;
|
||||||
|
try {
|
||||||
|
final Uri uri = Uri.parse(url);
|
||||||
|
|
||||||
final segments = uri.pathSegments;
|
final segments = uri.pathSegments;
|
||||||
for (int i = 0; i < segments.length; ++i) {
|
for (int i = 0; i < segments.length; ++i) {
|
||||||
switch (segments[i]) {
|
switch (segments[i]) {
|
||||||
case 'artists':
|
case 'artists':
|
||||||
result = FunkObject(
|
result = _makeParseResultFromEntityInfo(
|
||||||
kind: FunkEntity.artist,
|
FunkEntity.artist,
|
||||||
id: segments[i + 1],
|
uri.host,
|
||||||
domain: uri.host,
|
segments[i + 1],
|
||||||
|
);
|
||||||
|
++i;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'tracks':
|
||||||
|
result = _makeParseResultFromEntityInfo(
|
||||||
|
FunkEntity.track,
|
||||||
|
uri.host,
|
||||||
|
segments[i + 1],
|
||||||
);
|
);
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'albums':
|
case 'albums':
|
||||||
result = FunkObject(
|
result = _makeParseResultFromEntityInfo(
|
||||||
kind: FunkEntity.album,
|
FunkEntity.album,
|
||||||
id: segments[i + 1],
|
uri.host,
|
||||||
domain: uri.host,
|
segments[i + 1],
|
||||||
);
|
);
|
||||||
++i;
|
++i;
|
||||||
break;
|
break;
|
||||||
|
@ -40,12 +276,52 @@ FunkObject extract(final List<String> args) {
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e.toString());
|
console.error(e.toString());
|
||||||
result = FunkObject(
|
|
||||||
kind: FunkEntity.error,
|
|
||||||
id: '',
|
|
||||||
domain: '',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StageResult _onNothingStage(
|
||||||
|
final String arg,
|
||||||
|
final ParseResult previousResult,
|
||||||
|
final ParsingStage previousStage,
|
||||||
|
) {
|
||||||
|
ParsingStage currentStage = previousStage;
|
||||||
|
ParseResult result = previousResult;
|
||||||
|
switch (arg) {
|
||||||
|
case '-A':
|
||||||
|
case '--artist':
|
||||||
|
currentStage = ParsingStage.artist;
|
||||||
|
break;
|
||||||
|
case '-a':
|
||||||
|
case '--album':
|
||||||
|
currentStage = ParsingStage.album;
|
||||||
|
break;
|
||||||
|
case '-t':
|
||||||
|
case '--track':
|
||||||
|
currentStage = ParsingStage.track;
|
||||||
|
break;
|
||||||
|
case '-u':
|
||||||
|
case '--upload':
|
||||||
|
currentStage = ParsingStage.upload;
|
||||||
|
break;
|
||||||
|
case '-p':
|
||||||
|
case '--path':
|
||||||
|
currentStage = ParsingStage.path;
|
||||||
|
break;
|
||||||
|
case '-d':
|
||||||
|
case '--domain':
|
||||||
|
currentStage = ParsingStage.domain;
|
||||||
|
break;
|
||||||
|
case '-h':
|
||||||
|
case '--help':
|
||||||
|
_onHelpStage();
|
||||||
|
currentStage = ParsingStage.nothing;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = _parseUrl(arg, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StageResult(result: result, stage: currentStage);
|
||||||
|
}
|
||||||
|
|
|
@ -1,125 +0,0 @@
|
||||||
import 'package:funkblubber/funkentity.dart';
|
|
||||||
import 'package:funkblubber/console.dart' as console;
|
|
||||||
import 'package:funkblubber/parsing/parsing_stage.dart';
|
|
||||||
|
|
||||||
enum Action {
|
|
||||||
download,
|
|
||||||
upload,
|
|
||||||
}
|
|
||||||
|
|
||||||
class StageResult {
|
|
||||||
StageResult({required this.result, required this.stage});
|
|
||||||
final ParseResult result;
|
|
||||||
final ParsingStage stage;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParseResult {
|
|
||||||
ParseResult({
|
|
||||||
required this.success,
|
|
||||||
required this.action,
|
|
||||||
this.object,
|
|
||||||
this.localPath,
|
|
||||||
});
|
|
||||||
|
|
||||||
final FunkObject? object;
|
|
||||||
final String? localPath;
|
|
||||||
final Action action;
|
|
||||||
final bool success;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseResult extract(final List<String> args) {
|
|
||||||
ParseResult result = ParseResult(
|
|
||||||
success: false,
|
|
||||||
action: Action.download,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (args.isEmpty) {
|
|
||||||
console.error('no arguments provided');
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParsingStage currentStage = ParsingStage.nothing;
|
|
||||||
|
|
||||||
for (final String arg in args) {
|
|
||||||
switch (currentStage) {
|
|
||||||
case ParsingStage.nothing:
|
|
||||||
final stageResult = _onNothingStage(arg, result, currentStage);
|
|
||||||
currentStage = stageResult.stage;
|
|
||||||
result = stageResult.result;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error('not implemented yet');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
StageResult _onNothingStage(
|
|
||||||
final String arg,
|
|
||||||
final ParseResult previousResult,
|
|
||||||
final ParsingStage previousStage,
|
|
||||||
) {
|
|
||||||
ParsingStage currentStage = previousStage;
|
|
||||||
ParseResult result = previousResult;
|
|
||||||
switch (arg) {
|
|
||||||
case '-A':
|
|
||||||
case '--artist':
|
|
||||||
currentStage = ParsingStage.artist;
|
|
||||||
break;
|
|
||||||
case '-a':
|
|
||||||
case '--album':
|
|
||||||
currentStage = ParsingStage.album;
|
|
||||||
break;
|
|
||||||
case '-u':
|
|
||||||
case '--upload':
|
|
||||||
currentStage = ParsingStage.upload;
|
|
||||||
break;
|
|
||||||
case '-p':
|
|
||||||
case '--path':
|
|
||||||
currentStage = ParsingStage.path;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
try {
|
|
||||||
final Uri uri = Uri.parse(arg);
|
|
||||||
|
|
||||||
final segments = uri.pathSegments;
|
|
||||||
for (int i = 0; i < segments.length; ++i) {
|
|
||||||
switch (segments[i]) {
|
|
||||||
case 'artists':
|
|
||||||
result = ParseResult(
|
|
||||||
action: Action.download,
|
|
||||||
success: true,
|
|
||||||
object: FunkObject(
|
|
||||||
kind: FunkEntity.artist,
|
|
||||||
id: segments[i + 1],
|
|
||||||
domain: uri.host,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
++i;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'albums':
|
|
||||||
result = ParseResult(
|
|
||||||
action: Action.download,
|
|
||||||
success: true,
|
|
||||||
object: FunkObject(
|
|
||||||
kind: FunkEntity.album,
|
|
||||||
id: segments[i + 1],
|
|
||||||
domain: uri.host,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
++i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e.toString());
|
|
||||||
currentStage = ParsingStage.nothing;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return StageResult(result: result, stage: currentStage);
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
enum ParsingStage {
|
|
||||||
nothing,
|
|
||||||
album,
|
|
||||||
artist,
|
|
||||||
song,
|
|
||||||
path,
|
|
||||||
upload,
|
|
||||||
}
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
String cutTrailingDash(final String path) =>
|
||||||
|
path.endsWith('/') && path.length > 1
|
||||||
|
? path.substring(0, path.length - 1)
|
||||||
|
: path;
|
||||||
|
|
||||||
|
String sanitizePath(final String rawPath) => rawPath.replaceAll('/', '\\');
|
Loading…
Reference in New Issue